Switch folly to using a submodule instead of a fork.

To sync the folly submodule the first time, you'll need
to issue:

git submodule init
git submodule update

From then on, you'll also want to make sure folly is up to
date by issuing `git submodule update` after a `git pull`.
Esse commit está contido em:
Sara Golemon
2013-10-25 10:52:49 -07:00
commit 2ecabd236d
173 arquivos alterados com 57 adições e 52035 exclusões
+3
Ver Arquivo
@@ -0,0 +1,3 @@
[submodule "hphp/submodules/folly"]
path = hphp/submodules/folly
url = git://github.com/facebook/folly.git
+31
Ver Arquivo
@@ -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 <time.h>
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()
+1
Ver Arquivo
@@ -16,6 +16,7 @@
#
include(HPHPSetup)
include(FollySetup)
# HHVM Build
SET(USE_HHVM TRUE)
+13 -14
Ver Arquivo
@@ -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})
externo Link simbólico
+1
Ver Arquivo
@@ -0,0 +1 @@
../../submodules/folly/folly
-111
Ver Arquivo
@@ -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<int>(), std::make_tuple(12, 12));
* ASSERT(x == 24);
*/
#ifndef FOLLY_APPLYTUPLE_H_
#define FOLLY_APPLYTUPLE_H_
#include <tuple>
#include <functional>
#include <type_traits>
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<class F> F& makeCallable(F& f) { return f; }
template<class R, class C, class ...A>
auto makeCallable(R (C::*d)(A...)) -> decltype(std::mem_fn(d)) {
return std::mem_fn(d);
}
template<class Tuple>
struct DerefSize
: std::tuple_size<typename std::remove_reference<Tuple>::type>
{};
// CallTuple recursively unpacks tuple arguments so we can forward
// them into the function.
template<class Ret>
struct CallTuple {
template<class F, class Tuple, class ...Unpacked>
static typename std::enable_if<
(sizeof...(Unpacked) < DerefSize<Tuple>::value),
Ret
>::type call(const F& f, Tuple&& t, Unpacked&&... unp) {
typedef typename std::tuple_element<
sizeof...(Unpacked),
typename std::remove_reference<Tuple>::type
>::type ElementType;
return CallTuple<Ret>::call(f, std::forward<Tuple>(t),
std::forward<Unpacked>(unp)...,
std::forward<ElementType>(std::get<sizeof...(Unpacked)>(t))
);
}
template<class F, class Tuple, class ...Unpacked>
static typename std::enable_if<
(sizeof...(Unpacked) == DerefSize<Tuple>::value),
Ret
>::type call(const F& f, Tuple&& t, Unpacked&&... unp) {
return makeCallable(f)(std::forward<Unpacked>(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<class F, class Args> struct ReturnValue {};
template<class F, class ...Args>
struct ReturnValue<F,std::tuple<Args...>> {
typedef typename std::result_of<F (Args...)>::type type;
};
}
//////////////////////////////////////////////////////////////////////
template<class Callable, class Tuple>
typename detail::ReturnValue<
typename std::decay<Callable>::type,
typename std::remove_reference<Tuple>::type
>::type
applyTuple(const Callable& c, Tuple&& t) {
typedef typename detail::ReturnValue<
typename std::decay<Callable>::type,
typename std::remove_reference<Tuple>::type
>::type RetT;
return detail::CallTuple<RetT>::call(c, std::forward<Tuple>(t));
}
//////////////////////////////////////////////////////////////////////
}
#endif
-94
Ver Arquivo
@@ -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 <class Alloc>
std::pair<typename Arena<Alloc>::Block*, size_t>
Arena<Alloc>::Block::allocate(Alloc& alloc, size_t size, bool allowSlack) {
size_t allocSize = sizeof(Block) + size;
if (allowSlack) {
allocSize = ArenaAllocatorTraits<Alloc>::goodSize(alloc, allocSize);
}
void* mem = alloc.allocate(allocSize);
assert(isAligned(mem));
return std::make_pair(new (mem) Block(), allocSize - sizeof(Block));
}
template <class Alloc>
void Arena<Alloc>::Block::deallocate(Alloc& alloc) {
this->~Block();
alloc.deallocate(this);
}
template <class Alloc>
void* Arena<Alloc>::allocateSlow(size_t size) {
std::pair<Block*, size_t> 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 <class Alloc>
void Arena<Alloc>::merge(Arena<Alloc>&& other) {
blocks_.splice_after(blocks_.before_begin(), other.blocks_);
other.blocks_.clear();
other.ptr_ = other.end_ = nullptr;
totalAllocatedSize_ += other.totalAllocatedSize_;
other.totalAllocatedSize_ = 0;
}
template <class Alloc>
Arena<Alloc>::~Arena() {
auto disposer = [this] (Block* b) { b->deallocate(this->alloc()); };
while (!blocks_.empty()) {
blocks_.pop_front_and_dispose(disposer);
}
}
} // namespace folly
-248
Ver Arquivo
@@ -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 <cassert>
#include <utility>
#include <limits>
#include <boost/intrusive/slist.hpp>
#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 <class Alloc> struct ArenaAllocatorTraits;
template <class Alloc>
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<Arena>> 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<Block*, size_t> allocate(
Alloc& alloc, size_t size, bool allowSlack);
void deallocate(Alloc& alloc);
char* start() {
return reinterpret_cast<char*>(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<uintptr_t>(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<true> 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<Block, BlockLink, &Block::link>,
boost::intrusive::constant_time_size<false>,
boost::intrusive::cache_last<true>> 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 <class Alloc>
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<SysAlloc> {
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<SysAlloc> {
public:
explicit SysArena(
size_t minBlockSize = kDefaultMinBlockSize,
size_t sizeLimit = 0)
: Arena<SysAlloc>(SysAlloc(), minBlockSize, sizeLimit) {
}
};
} // namespace folly
#include "folly/Arena-inl.h"
#endif /* FOLLY_ARENA_H_ */
-379
Ver Arquivo
@@ -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 <class KeyT, class ValueT, class HashFcn, class EqualFcn>
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::
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 <class KeyT, class ValueT, class HashFcn, class EqualFcn>
typename AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::SimpleRetT
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::
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 <class KeyT, class ValueT, class HashFcn, class EqualFcn>
template <class T>
typename AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::SimpleRetT
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::
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<T>(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 <class KeyT, class ValueT, class HashFcn, class EqualFcn>
size_t AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::
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 <class KeyT, class ValueT, class HashFcn, class EqualFcn>
const typename AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::Config
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::defaultConfig;
template <class KeyT, class ValueT, class HashFcn, class EqualFcn>
typename AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::SmartPtr
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::
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<void, void(*)(void*)> mem(malloc(sz), free);
new(mem.get()) AtomicHashArray(capacity, c.emptyKey, c.lockedKey, c.erasedKey,
c.maxLoadFactor, c.entryCountThreadCacheSize);
SmartPtr map(static_cast<AtomicHashArray*>(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 <class KeyT, class ValueT, class HashFcn, class EqualFcn>
void AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::
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 <class KeyT, class ValueT, class HashFcn, class EqualFcn>
void AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::
clear() {
FOR_EACH_RANGE(i, 0, capacity_) {
if (cells_[i].first != kEmptyKey_) {
cells_[i].~value_type();
*const_cast<KeyT*>(&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 <class KeyT, class ValueT, class HashFcn, class EqualFcn>
template <class ContT, class IterVal>
struct AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn>::aha_iterator
: boost::iterator_facade<aha_iterator<ContT,IterVal>,
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<class OtherContT, class OtherVal>
aha_iterator(const aha_iterator<OtherContT,OtherVal>& o,
typename std::enable_if<
std::is_convertible<OtherVal*,IterVal*>::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
-297
Ver Arquivo
@@ -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 <sahrens@fb.com>
* @author Jordan DeLong <delong.j@fb.com>
*/
#ifndef FOLLY_ATOMICHASHARRAY_H_
#define FOLLY_ATOMICHASHARRAY_H_
#include <atomic>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/noncopyable.hpp>
#include "folly/Hash.h"
#include "folly/ThreadCachedInt.h"
namespace folly {
template <class KeyT, class ValueT,
class HashFcn = std::hash<KeyT>, class EqualFcn = std::equal_to<KeyT>>
class AtomicHashMap;
template <class KeyT, class ValueT,
class HashFcn = std::hash<KeyT>, class EqualFcn = std::equal_to<KeyT>>
class AtomicHashArray : boost::noncopyable {
static_assert((std::is_convertible<KeyT,int32_t>::value ||
std::is_convertible<KeyT,int64_t>::value ||
std::is_convertible<KeyT,const void*>::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<const KeyT, ValueT> 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<class ContT, class IterVal>
struct aha_iterator;
typedef aha_iterator<const AtomicHashArray,const value_type> const_iterator;
typedef aha_iterator<AtomicHashArray,value_type> 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<AtomicHashArray, Deleter> 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<AtomicHashArray*>(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<iterator,bool> insert(const value_type& r) {
SimpleRetT ret = insertInternal(r.first, r.second);
return std::make_pair(iterator(this, ret.idx), ret.success);
}
std::pair<iterator,bool> 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<AtomicHashArray*>(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<KeyT,ValueT,HashFcn,EqualFcn>;
struct SimpleRetT { size_t idx; bool success;
SimpleRetT(size_t i, bool s) : idx(i), success(s) {}
SimpleRetT() {}
};
template <class T>
SimpleRetT insertInternal(KeyT key, T&& value);
SimpleRetT findInternal(const KeyT key);
static std::atomic<KeyT>* cellKeyPtr(const value_type& r) {
// We need some illegal casting here in order to actually store
// our value_type as a std::pair<const,>. But a little bit of
// undefined behavior never hurt anyone ...
static_assert(sizeof(std::atomic<KeyT>) == sizeof(KeyT),
"std::atomic is implemented in an unexpected way for AHM");
return
const_cast<std::atomic<KeyT>*>(
reinterpret_cast<std::atomic<KeyT> 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<int64_t> numEntries_; // Successful key inserts
ThreadCachedInt<int64_t> numPendingEntries_; // Used by insertInternal
std::atomic<int64_t> isFull_; // Used by insertInternal
std::atomic<int64_t> 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_
-413
Ver Arquivo
@@ -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 <class KeyT, class ValueT, class HashFcn, class EqualFcn>
const typename AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::Config
AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::defaultConfig;
// AtomicHashMap constructor -- Atomic wrapper that allows growth
// This class has a lot of overhead (184 Bytes) so only use for big maps
template <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
std::pair<typename AtomicHashMap<KeyT,ValueT,HashFcn,EqualFcn>::iterator,bool>
AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
std::pair<typename AtomicHashMap<KeyT,ValueT,HashFcn,EqualFcn>::iterator,bool>
AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
template <class T>
typename AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::SimpleRetT
AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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<T>(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<T>(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 KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
typename AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::iterator
AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
typename AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::const_iterator
AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
find(KeyT k) const {
return const_cast<AtomicHashMap*>(this)->find(k);
}
// findInternal --
template <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
typename AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::SimpleRetT
AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
typename AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::SimpleRetT
AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
typename AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::size_type
AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
size_t AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
size_t AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
void AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
size_t AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
inline uint32_t AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::
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 <typename KeyT, typename ValueT, typename HashFcn, typename EqualFcn>
template<class ContT, class IterVal, class SubIt>
struct AtomicHashMap<KeyT, ValueT, HashFcn, EqualFcn>::ahm_iterator
: boost::iterator_facade<ahm_iterator<ContT,IterVal,SubIt>,
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<class OtherContT, class OtherVal, class OtherSubIt>
ahm_iterator(const ahm_iterator<OtherContT,OtherVal,OtherSubIt>& o,
typename std::enable_if<
std::is_convertible<OtherSubIt,SubIt>::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
-412
Ver Arquivo
@@ -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 <int64, int64> 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 <sahrens@fb.com>
* @author Jordan DeLong <delong.j@fb.com>
*
*/
#ifndef FOLLY_ATOMICHASHMAP_H_
#define FOLLY_ATOMICHASHMAP_H_
#include <boost/iterator/iterator_facade.hpp>
#include <boost/noncopyable.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <stdexcept>
#include <functional>
#include <atomic>
#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 KeyT, class ValueT, class HashFcn, class EqualFcn>
class AtomicHashMap : boost::noncopyable {
typedef AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn> SubMap;
public:
typedef KeyT key_type;
typedef ValueT mapped_type;
typedef std::pair<const KeyT, ValueT> 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<class ContT, class IterVal, class SubIt>
struct ahm_iterator;
typedef ahm_iterator<const AtomicHashMap,
const value_type,
typename SubMap::const_iterator>
const_iterator;
typedef ahm_iterator<AtomicHashMap,
value_type,
typename SubMap::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<iterator,bool> insert(const value_type& r) {
return insert(r.first, r.second);
}
std::pair<iterator,bool> insert(key_type k, const mapped_type& v);
std::pair<iterator,bool> insert(value_type&& r) {
return insert(r.first, std::move(r.second));
}
std::pair<iterator,bool> 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<AtomicHashMap*>(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 <class T>
SimpleRetT insertInternal(KeyT key, T&& value);
SimpleRetT findInternal(const KeyT k) const;
SimpleRetT findAtInternal(uint32_t idx) const;
std::atomic<SubMap*> subMaps_[kNumSubMaps_];
std::atomic<uint32_t> 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_
-94
Ver Arquivo
@@ -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__ */
-558
Ver Arquivo
@@ -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 <byteswap.h>
#endif
#include <cassert>
#include <cinttypes>
#include <iterator>
#include <limits>
#include <type_traits>
#include <boost/iterator/iterator_adaptor.hpp>
#include <stdint.h>
namespace folly {
// Generate overloads for findFirstSet as wrappers around
// appropriate ffs, ffsl, ffsll gcc builtins
template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) <= sizeof(unsigned int)),
unsigned int>::type
findFirstSet(T x) {
return __builtin_ffs(x);
}
template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) > sizeof(unsigned int) &&
sizeof(T) <= sizeof(unsigned long)),
unsigned int>::type
findFirstSet(T x) {
return __builtin_ffsl(x);
}
template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) > sizeof(unsigned long) &&
sizeof(T) <= sizeof(unsigned long long)),
unsigned int>::type
findFirstSet(T x) {
return __builtin_ffsll(x);
}
template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value && std::is_signed<T>::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<typename std::make_unsigned<T>::type>(x));
}
// findLastSet: return the 1-based index of the highest bit set
// for x > 0, findLastSet(x) == 1 + floor(log2(x))
template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) <= sizeof(unsigned int)),
unsigned int>::type
findLastSet(T x) {
return x ? 8 * sizeof(unsigned int) - __builtin_clz(x) : 0;
}
template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::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 <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::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 <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value &&
std::is_signed<T>::value),
unsigned int>::type
findLastSet(T x) {
return findLastSet(static_cast<typename std::make_unsigned<T>::type>(x));
}
template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
std::is_integral<T>::value && std::is_unsigned<T>::value,
T>::type
nextPowTwo(T v) {
return v ? (1ul << findLastSet(v - 1)) : 1;
}
template <class T>
inline constexpr
typename std::enable_if<
std::is_integral<T>::value && std::is_unsigned<T>::value,
bool>::type
isPowTwo(T v) {
return (v != 0) && !(v & (v - 1));
}
/**
* Population count
*/
template <class T>
inline typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) <= sizeof(unsigned int)),
size_t>::type
popcount(T x) {
return detail::popcount(x);
}
template <class T>
inline typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::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 <class T>
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<class Int16>
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<t>::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 <class T>
struct EndianInt : public detail::EndianIntBase<T> {
public:
static T big(T x) { return EndianInt::swap(x); }
static T little(T x) { return x; }
};
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
template <class T>
struct EndianInt : public detail::EndianIntBase<T> {
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<t>(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 <class T> static T swap(T x) {
return detail::EndianInt<T>::swap(x);
}
template <class T> static T big(T x) {
return detail::EndianInt<T>::big(x);
}
template <class T> static T little(T x) {
return detail::EndianInt<T>::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 BaseIter> class BitIterator;
template <class BaseIter>
BitIterator<BaseIter> findFirstSet(BitIterator<BaseIter>,
BitIterator<BaseIter>);
/**
* 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 BaseIter>
class BitIterator
: public bititerator_detail::BitIteratorBase<BaseIter>::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<BaseIter>::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<BaseIter>::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<BaseIter>::reference,
typename std::iterator_traits<BaseIter>::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 <class BaseIter>
BitIterator<BaseIter> makeBitIterator(const BaseIter& iter) {
return BitIterator<BaseIter>(iter);
}
/**
* Find first bit set in a range of bit iterators.
* 4.5x faster than the obvious std::find(begin, end, true);
*/
template <class BaseIter>
BitIterator<BaseIter> findFirstSet(BitIterator<BaseIter> begin,
BitIterator<BaseIter> 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 <class T, class Enable=void> struct Unaligned;
/**
* Representation of an unaligned value of a POD type.
*/
template <class T>
struct Unaligned<
T,
typename std::enable_if<std::is_pod<T>::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 <class T>
inline T loadUnaligned(const void* p) {
static_assert(sizeof(Unaligned<T>) == sizeof(T), "Invalid unaligned size");
static_assert(alignof(Unaligned<T>) == 1, "Invalid alignment");
return static_cast<const Unaligned<T>*>(p)->value;
}
/**
* Write an unaligned value of type T.
*/
template <class T>
inline void storeUnaligned(void* p, T value) {
static_assert(sizeof(Unaligned<T>) == sizeof(T), "Invalid unaligned size");
static_assert(alignof(Unaligned<T>) == 1, "Invalid alignment");
new (p) Unaligned<T>(value);
}
} // namespace folly
#endif /* FOLLY_BITS_H_ */
-33
Ver Arquivo
@@ -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 <chrono> that hides away some gcc 4.6 issues
#ifndef FOLLY_CHRONO_H_
#define FOLLY_CHRONO_H_
#include <chrono>
#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_ */
-216
Ver Arquivo
@@ -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 <xliux@fb.com>
#ifndef FOLLY_CONCURRENTSKIPLIST_INL_H_
#define FOLLY_CONCURRENTSKIPLIST_INL_H_
#include <algorithm>
#include <climits>
#include <cmath>
#include <boost/random.hpp>
#include <glog/logging.h>
#include "folly/SmallLocks.h"
#include "folly/ThreadLocal.h"
namespace folly { namespace detail {
template<typename ValT, typename NodeT> class csl_iterator;
template<typename T>
class SkipListNode : boost::noncopyable {
enum {
IS_HEAD_NODE = 1,
MARKED_FOR_REMOVAL = (1 << 1),
FULLY_LINKED = (1 << 2),
};
public:
typedef T value_type;
template<typename U,
typename=typename std::enable_if<std::is_convertible<U, T>::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<SkipListNode*>);
auto* node = static_cast<SkipListNode*>(malloc(size));
// do placement new
new (node) SkipListNode(height, std::forward<U>(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<MicroSpinLock> acquireGuard() {
return std::unique_lock<MicroSpinLock>(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<typename U>
SkipListNode(uint8_t height, U&& data, bool isHead) :
height_(height), data_(std::forward<U>(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<SkipListNode*>(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<uint16_t> flags_;
const uint8_t height_;
MicroSpinLock spinLock_;
value_type data_;
std::atomic<SkipListNode*> 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<size_t>::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<size_t>(sizeLimit);
}
lookupTable_[kMaxHeight - 1] = 1;
sizeLimitTable_[kMaxHeight - 1] = kMaxSizeLimit;
}
static double randomProb() {
static ThreadLocal<boost::lagged_fibonacci2281> rng_;
return (*rng_)();
}
double lookupTable_[kMaxHeight];
size_t sizeLimitTable_[kMaxHeight];
};
}}
#endif // FOLLY_CONCURRENTSKIPLIST_INL_H_
-859
Ver Arquivo
@@ -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 <xliux@fb.com>
//
// 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<int> SkipListT;
shared_ptr<SkipListT> 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 <algorithm>
#include <climits>
#include <type_traits>
#include <utility>
#include <vector>
#include <atomic>
#include <thread>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <glog/logging.h>
#include "folly/ConcurrentSkipList-inl.h"
#include "folly/Likely.h"
#include "folly/SmallLocks.h"
namespace folly {
template<typename T, typename Comp=std::less<T>, 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<folly::MicroSpinLock> ScopedLocker;
typedef ConcurrentSkipList<T, Comp, MAX_HEIGHT> SkipListType;
public:
typedef detail::SkipListNode<T> NodeType;
typedef T value_type;
typedef T key_type;
typedef detail::csl_iterator<value_type, NodeType> iterator;
typedef detail::csl_iterator<const value_type, const NodeType> 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<SkipListType> createInstance(int height=1) {
return boost::shared_ptr<SkipListType>(new SkipListType(height));
}
// create a unique_ptr skiplist object with initial head height.
static std::unique_ptr<SkipListType> createRawInstance(int height=1) {
return std::unique_ptr<SkipListType>(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<MicroSpinLock> g(lock_);
if (nodes_.get() == nullptr) {
nodes_.reset(new std::vector<NodeType*>(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<std::vector<NodeType*> > newNodes;
{
std::lock_guard<MicroSpinLock> 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<std::vector<NodeType*>> nodes_;
std::atomic<int32_t> refs_; // current number of visitors to the list
std::atomic<bool> 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<typename U>
std::pair<NodeType*, size_t> 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<U>(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<NodeType*, int> 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<NodeType*, int> 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<NodeType*, int> 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<NodeType*> head_;
Recycler recycler_;
std::atomic<size_t> size_;
};
template<typename T, typename Comp, int MAX_HEIGHT>
class ConcurrentSkipList<T, Comp, MAX_HEIGHT>::Accessor {
typedef detail::SkipListNode<T> NodeType;
typedef ConcurrentSkipList<T, Comp, MAX_HEIGHT> 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<ConcurrentSkipList> 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<size_type>::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<typename U,
typename=typename std::enable_if<std::is_convertible<U, T>::value>::type>
std::pair<iterator, bool> insert(U&& data) {
auto ret = sl_->addOrGetData(std::forward<U>(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<key_type*, bool> 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<SkipListType> slHolder_;
};
// implements forward iterator concept.
template<typename ValT, typename NodeT>
class detail::csl_iterator :
public boost::iterator_facade<csl_iterator<ValT, NodeT>,
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<typename OtherVal, typename OtherNode>
csl_iterator(const csl_iterator<OtherVal, OtherNode> &other,
typename std::enable_if<std::is_convertible<OtherVal, ValT>::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<class,class> 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<typename T, typename Comp, int MAX_HEIGHT>
class ConcurrentSkipList<T, Comp, MAX_HEIGHT>::Skipper {
typedef detail::SkipListNode<T> NodeType;
typedef ConcurrentSkipList<T, Comp, MAX_HEIGHT> 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<SkipListType>& 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_
-136
Ver Arquivo
@@ -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<bool>::value = "true";
template <> const char *const MaxString<uint8_t>::value = "255";
template <> const char *const MaxString<uint16_t>::value = "65535";
template <> const char *const MaxString<uint32_t>::value = "4294967295";
#if __SIZEOF_LONG__ == 4
template <> const char *const MaxString<unsigned long>::value =
"4294967295";
#else
template <> const char *const MaxString<unsigned long>::value =
"18446744073709551615";
#endif
static_assert(sizeof(unsigned long) >= 4,
"Wrong value for MaxString<unsigned long>::value,"
" please update.");
template <> const char *const MaxString<unsigned long long>::value =
"18446744073709551615";
static_assert(sizeof(unsigned long long) >= 8,
"Wrong value for MaxString<unsigned long long>::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<uint8_t>(&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
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
-119
Ver Arquivo
@@ -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 <cstdint>
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_ */
-221
Ver Arquivo
@@ -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 <limits>
#include <stdexcept>
#include <glog/logging.h>
#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<Types...> 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 <typename... Types>
class DiscriminatedPtr {
// <, not <=, as our indexes are 1-based (0 means "empty")
static_assert(sizeof...(Types) < std::numeric_limits<uint16_t>::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 <typename T>
explicit DiscriminatedPtr(T* ptr) {
set(ptr, typeIndex<T>());
}
/**
* 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 <typename T>
void set(T* ptr) {
set(ptr, typeIndex<T>());
}
/**
* 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 <typename T>
T* get_nothrow() noexcept {
void* p = LIKELY(hasType<T>()) ? ptr() : nullptr;
return static_cast<T*>(p);
}
template <typename T>
const T* get_nothrow() const noexcept {
const void* p = LIKELY(hasType<T>()) ? ptr() : nullptr;
return static_cast<const T*>(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 <typename T>
T* get() {
if (UNLIKELY(!hasType<T>())) {
throw std::invalid_argument("Invalid type");
}
return static_cast<T*>(ptr());
}
template <typename T>
const T* get() const {
if (UNLIKELY(!hasType<T>())) {
throw std::invalid_argument("Invalid type");
}
return static_cast<const T*>(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 <typename T>
bool hasType() const {
return index() == typeIndex<T>();
}
/**
* Clear this DiscriminatedPtr, making it empty.
*/
void clear() {
data_ = 0;
}
/**
* Assignment operator from a pointer of type T.
*/
template <typename T>
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 V>
typename dptr_detail::VisitorResult<V, Types...>::type apply(V&& visitor) {
size_t n = index();
if (n == 0) throw std::invalid_argument("Empty DiscriminatedPtr");
return dptr_detail::ApplyVisitor<V, Types...>()(
n, std::forward<V>(visitor), ptr());
}
template <typename V>
typename dptr_detail::ConstVisitorResult<V, Types...>::type apply(V&& visitor)
const {
size_t n = index();
if (n == 0) throw std::invalid_argument("Empty DiscriminatedPtr");
return dptr_detail::ApplyConstVisitor<V, Types...>()(
n, std::forward<V>(visitor), ptr());
}
private:
/**
* Get the 1-based type index of T in Types.
*/
template <typename T>
size_t typeIndex() const {
return dptr_detail::GetTypeIndex<T, Types...>::value;
}
uint16_t index() const { return data_ >> 48; }
void* ptr() const {
return reinterpret_cast<void*>(data_ & ((1ULL << 48) - 1));
}
void set(void* p, uint16_t v) {
uintptr_t ip = reinterpret_cast<uintptr_t>(p);
CHECK(!(ip >> 48));
ip |= static_cast<uintptr_t>(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_ */
-328
Ver Arquivo
@@ -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 <njormrod@fb.com>
#ifndef DYNAMIC_CONVERTER_H
#define DYNAMIC_CONVERTER_H
#include "folly/dynamic.h"
namespace folly {
template <typename T> T convertTo(const dynamic&);
template <typename T> 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<fbvector<fbvector<int>>>(d);
*
* See docs/DynamicConverter.md for supported types and customization
*/
#include <type_traits>
#include <iterator>
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/mpl/has_xxx.hpp>
#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 <typename T> struct class_is_container {
typedef std::reverse_iterator<T*> some_iterator;
enum { value = has_value_type<T>::value &&
has_iterator<T>::value &&
std::is_constructible<T, some_iterator, some_iterator>::value };
};
template <typename T> struct class_is_range {
enum { value = has_value_type<T>::value &&
has_iterator<T>::value };
};
template <typename T> struct is_container
: std::conditional<
std::is_class<T>::value,
class_is_container<T>,
std::false_type
>::type {};
template <typename T> struct is_range
: std::conditional<
std::is_class<T>::value,
class_is_range<T>,
std::false_type
>::type {};
template <typename T> struct is_associative_container
: std::integral_constant<
bool,
is_range<T>::value && has_mapped_type<T>::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<typename T>
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<T>(*it));
}
};
template<typename F, typename S>
struct Dereferencer<std::pair<F, S>> {
static inline void
derefToCache(std::pair<F, S>* mem, const dynamic::const_item_iterator& it) {
new (mem) std::pair<F, S>(
convertTo<F>(it->first), convertTo<S>(it->second)
);
}
// Intentional duplication of the code in Dereferencer
template <typename T>
static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
new (mem) T(convertTo<T>(*it));
}
};
template <typename T, typename It>
class Transformer : public boost::iterator_adaptor<
Transformer<T, It>,
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<ttype>::derefToCache(&cache_, this->base_reference());
valid_ = true;
}
return cache_;
}
public:
explicit Transformer(const It& it)
: Transformer::iterator_adaptor_(it), valid_(false) {}
};
// conversion factory
template <typename T, typename It>
static inline std::move_iterator<Transformer<T, It>>
conversionIterator(const It& it) {
return std::make_move_iterator(Transformer<T, It>(it));
}
} // namespace dynamicconverter_detail
///////////////////////////////////////////////////////////////////////////////
// DynamicConverter specializations
template <typename T, typename Enable = void> struct DynamicConverter;
/**
* Each specialization of DynamicConverter has the function
* 'static T convert(const dynamic& d);'
*/
// boolean
template <>
struct DynamicConverter<bool> {
static bool convert(const dynamic& d) {
return d.asBool();
}
};
// integrals
template <typename T>
struct DynamicConverter<T,
typename std::enable_if<std::is_integral<T>::value &&
!std::is_same<T, bool>::value>::type> {
static T convert(const dynamic& d) {
return static_cast<T>(d.asInt());
}
};
// floating point
template <typename T>
struct DynamicConverter<T,
typename std::enable_if<std::is_floating_point<T>::value>::type> {
static T convert(const dynamic& d) {
return static_cast<T>(d.asDouble());
}
};
// fbstring
template <>
struct DynamicConverter<folly::fbstring> {
static folly::fbstring convert(const dynamic& d) {
return d.asString();
}
};
// std::string
template <>
struct DynamicConverter<std::string> {
static std::string convert(const dynamic& d) {
return d.asString().toStdString();
}
};
// std::pair
template <typename F, typename S>
struct DynamicConverter<std::pair<F,S>> {
static std::pair<F, S> convert(const dynamic& d) {
if (d.isArray() && d.size() == 2) {
return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1]));
} else if (d.isObject() && d.size() == 1) {
auto it = d.items().begin();
return std::make_pair(convertTo<F>(it->first), convertTo<S>(it->second));
} else {
throw TypeError("array (size 2) or object (size 1)", d.type());
}
}
};
// containers
template <typename C>
struct DynamicConverter<C,
typename std::enable_if<
dynamicconverter_detail::is_container<C>::value>::type> {
static C convert(const dynamic& d) {
if (d.isArray()) {
return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
dynamicconverter_detail::conversionIterator<C>(d.end()));
} else if (d.isObject()) {
return C(dynamicconverter_detail::conversionIterator<C>
(d.items().begin()),
dynamicconverter_detail::conversionIterator<C>
(d.items().end()));
} else {
throw TypeError("object or array", d.type());
}
}
};
template <typename C, typename Enable = void>
struct DynamicConstructor {
static dynamic construct(const C& x) {
return dynamic(x);
}
};
template<typename C>
struct DynamicConstructor<C,
typename std::enable_if<
dynamicconverter_detail::is_associative_container<C>::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<typename C>
struct DynamicConstructor<C,
typename std::enable_if<
!dynamicconverter_detail::is_associative_container<C>::value &&
!std::is_constructible<StringPiece, const C&>::value &&
dynamicconverter_detail::is_range<C>::value>::type> {
static dynamic construct(const C& x) {
dynamic d = {};
for (auto& item : x) {
d.push_back(toDynamic(item));
}
return d;
}
};
template<typename A, typename B>
struct DynamicConstructor<std::pair<A, B>, void> {
static dynamic construct(const std::pair<A, B>& x) {
dynamic d = {};
d.push_back(toDynamic(x.first));
d.push_back(toDynamic(x.second));
return d;
}
};
///////////////////////////////////////////////////////////////////////////////
// convertTo implementation
template <typename T>
T convertTo(const dynamic& d) {
return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
}
template<typename T>
dynamic toDynamic(const T& x) {
return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);
}
} // namespace folly
#endif // DYNAMIC_CONVERTER_H
-115
Ver Arquivo
@@ -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 <errno.h>
#include <cstdio>
#include <stdexcept>
#include <system_error>
#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 <class... Args>
void throwSystemErrorExplicit(int, Args&&... args) FOLLY_NORETURN;
template <class... Args>
void throwSystemErrorExplicit(int err, Args&&... args) {
throwSystemErrorExplicit(
err, to<fbstring>(std::forward<Args>(args)...).c_str());
}
// Helper to throw std::system_error from errno and components of a string
template <class... Args>
void throwSystemError(Args&&... args) FOLLY_NORETURN;
template <class... Args>
void throwSystemError(Args&&... args) {
throwSystemErrorExplicit(errno, std::forward<Args>(args)...);
}
// Check a Posix return code (0 on success, error number on error), throw
// on error.
template <class... Args>
void checkPosixError(int err, Args&&... args) {
if (UNLIKELY(err != 0)) {
throwSystemErrorExplicit(err, std::forward<Args>(args)...);
}
}
// Check a Linux kernel-style return code (>= 0 on success, negative error
// number on error), throw on error.
template <class... Args>
void checkKernelError(ssize_t ret, Args&&... args) {
if (UNLIKELY(ret < 0)) {
throwSystemErrorExplicit(-ret, std::forward<Args>(args)...);
}
}
// Check a traditional Unix return code (-1 and sets errno on error), throw
// on error.
template <class... Args>
void checkUnixError(ssize_t ret, Args&&... args) {
if (UNLIKELY(ret == -1)) {
throwSystemError(std::forward<Args>(args)...);
}
}
template <class... Args>
void checkUnixErrorExplicit(ssize_t ret, int savedErrno, Args&&... args) {
if (UNLIKELY(ret == -1)) {
throwSystemErrorExplicit(savedErrno, std::forward<Args>(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 <class... Args>
void checkFopenError(FILE* fp, Args&&... args) {
if (UNLIKELY(!fp)) {
throwSystemError(std::forward<Args>(args)...);
}
}
template <class... Args>
void checkFopenErrorExplicit(FILE* fp, int savedErrno, Args&&... args) {
if (UNLIKELY(!fp)) {
throwSystemErrorExplicit(savedErrno, std::forward<Args>(args)...);
}
}
} // namespace folly
#endif /* FOLLY_EXCEPTION_H_ */
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
-131
Ver Arquivo
@@ -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 <fcntl.h>
#include <unistd.h>
#include "folly/Exception.h"
#include "folly/FileUtil.h"
#include "folly/Format.h"
#include "folly/ScopeGuard.h"
#include <system_error>
#include <glog/logging.h>
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
-129
Ver Arquivo
@@ -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 <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
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_ */
-139
Ver Arquivo
@@ -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 <cerrno>
#ifdef __APPLE__
#include <fcntl.h>
#endif
#include <sys/file.h>
#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<void*>(buf), count);
}
ssize_t pwriteFull(int fd, const void* buf, size_t count, off_t offset) {
return wrapFull(pwrite, fd, const_cast<void*>(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
-105
Ver Arquivo
@@ -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 <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <unistd.h>
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_ */
-266
Ver Arquivo
@@ -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 <cstdint>
#include "folly/Range.h"
namespace folly {
namespace detail {
template <int BITS>
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 <int BITS>
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<BITS>::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<BITS>::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<BITS>::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_ */
-260
Ver Arquivo
@@ -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<data>::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 <boost/type_traits/remove_cv.hpp>
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<T>::type. Note that the function
* remove_cv_from_expression is never defined - use it only inside
* typeof.
*/
template <class T> typename boost::remove_cv<T>::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<string, string> 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 <typename T, typename U>
class HasLess {
struct BiggerThanChar { char unused[2]; };
template <typename C, typename D> static char test(decltype(C() < D())*);
template <typename, typename> static BiggerThanChar test(...);
public:
enum { value = sizeof(test<T, U>(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 <class T, class U>
typename std::enable_if<HasLess<T, U>::value, bool>::type
notThereYet(T& iter, const U& end) {
return iter < end;
}
template <class T, class U>
typename std::enable_if<!HasLess<T, U>::value, bool>::type
notThereYet(T& iter, const U& end) {
return iter != end;
}
#else
template <class T, class U>
typename std::enable_if<
(std::is_arithmetic<T>::value && std::is_arithmetic<U>::value) ||
(std::is_pointer<T>::value && std::is_pointer<U>::value),
bool>::type
notThereYet(T& iter, const U& end) {
return iter < end;
}
template <class T, class U>
typename std::enable_if<
!(
(std::is_arithmetic<T>::value && std::is_arithmetic<U>::value) ||
(std::is_pointer<T>::value && std::is_pointer<U>::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 <class T, class U>
typename std::enable_if<HasLess<U, T>::value, bool>::type
downTo(T& iter, const U& begin) {
return begin < iter--;
}
template <class T, class U>
typename std::enable_if<!HasLess<U, T>::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
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
-137
Ver Arquivo
@@ -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<const char*>(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<unsigned char>(p[1])]) !=
Align::INVALID) {
fill = *p;
align = a;
p += 2;
if (p == end) return;
} else if ((a = formatAlignTable[static_cast<unsigned char>(*p)]) !=
Align::INVALID) {
align = a;
if (++p == end) return;
}
Sign s;
unsigned char uSign = static_cast<unsigned char>(*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<int>(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<int>(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
-277
Ver Arquivo
@@ -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 <array>
#include <tuple>
#include <type_traits>
#include <vector>
#include <deque>
#include <map>
#include <unordered_map>
#include <double-conversion.h>
#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 <bool containerMode, class... Args> class Formatter;
template <class... Args>
Formatter<false, Args...> format(StringPiece fmt, Args&&... args);
template <class C>
Formatter<true, C> vformat(StringPiece fmt, C&& container);
template <class T, class Enable=void> 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 <bool containerMode, class... Args>
class Formatter {
template <class... A>
friend Formatter<false, A...> format(StringPiece fmt, A&&... arg);
template <class C>
friend Formatter<true, C> vformat(StringPiece fmt, C&& container);
public:
/**
* Append to output. out(StringPiece sp) may be called (more than once)
*/
template <class Output>
void operator()(Output& out) const;
/**
* Append to a string.
*/
template <class Str>
typename std::enable_if<detail::IsSomeString<Str>::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<FormatValue<
typename std::decay<Args>::type>...> ValueTuple;
static constexpr size_t valueCount = std::tuple_size<ValueTuple>::value;
template <size_t K, class Callback>
typename std::enable_if<K == valueCount>::type
doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const {
arg.error("argument index out of range, max=", i);
}
template <size_t K, class Callback>
typename std::enable_if<(K < valueCount)>::type
doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const {
if (i == K) {
std::get<K>(values_).format(arg, cb);
} else {
doFormatFrom<K+1>(i, arg, cb);
}
}
template <class Callback>
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<bool containerMode, class... Args>
std::ostream& operator<<(std::ostream& out,
const Formatter<containerMode, Args...>& 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 <class... Args>
Formatter<false, Args...> format(StringPiece fmt, Args&&... args) {
return Formatter<false, Args...>(
fmt, std::forward<Args>(args)...);
}
/**
* Create a formatter object that takes one argument (of container type)
* and uses that container to get argument values from.
*
* std::map<string, string> 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 <class Container>
Formatter<true, Container> vformat(StringPiece fmt, Container&& container) {
return Formatter<true, Container>(
fmt, std::forward<Container>(container));
}
/**
* Append formatted output to a string.
*
* std::string foo;
* format(&foo, "{} {}", 42, 23);
*
* Shortcut for toAppend(format(...), &foo);
*/
template <class Str, class... Args>
typename std::enable_if<detail::IsSomeString<Str>::value>::type
format(Str* out, StringPiece fmt, Args&&... args) {
format(fmt, std::forward<Args>(args)...).appendTo(*out);
}
/**
* Append vformatted output to a string.
*/
template <class Str, class Container>
typename std::enable_if<detail::IsSomeString<Str>::value>::type
vformat(Str* out, StringPiece fmt, Container&& container) {
vformat(fmt, std::forward<Container>(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 <class FormatCallback>
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 <class FormatCallback>
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 <class FormatCallback, bool containerMode, class... Args>
void formatFormatter(const Formatter<containerMode, Args...>& formatter,
FormatArg& arg,
FormatCallback& cb);
} // namespace format_value
/*
* Specialize folly::FormatValue for your type.
*
* FormatValue<T> 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 <class Callback>
* 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_ */
-256
Ver Arquivo
@@ -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 <stdexcept>
#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<std::string>.
*/
template <typename... Args>
void enforce(bool v, Args&&... args) const {
if (UNLIKELY(!v)) {
error(std::forward<Args>(args)...);
}
}
template <typename... Args>
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 <bool emptyOk=false>
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 <bool emptyOk>
StringPiece doSplitKey();
StringPiece key_;
int nextIntKey_;
StringPiece nextKey_;
enum class NextKeyMode {
NONE,
INT,
STRING,
};
NextKeyMode nextKeyMode_;
};
template <typename... Args>
inline void FormatArg::error(Args&&... args) const {
throw std::invalid_argument(to<std::string>(
"folly::format: invalid format argument {", fullArgString, "}: ",
std::forward<Args>(args)...));
}
template <bool emptyOk>
inline StringPiece FormatArg::splitKey() {
enforce(nextKeyMode_ != NextKeyMode::INT, "integer key expected");
return doSplitKey<emptyOk>();
}
template <bool emptyOk>
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<const char*>(memchr(b, '[', e - b));
enforce(p, "unmatched ']'");
} else {
p = static_cast<const char*>(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<int>(doSplitKey<true>());
} catch (const std::out_of_range& e) {
error("integer key required");
return 0; // unreached
}
}
} // namespace folly
#endif /* FOLLY_FORMATARG_H_ */
-33
Ver Arquivo
@@ -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
-608
Ver Arquivo
@@ -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 <cstdint>
#include <limits>
#include "folly/detail/GroupVarintDetail.h"
#include "folly/Bits.h"
#include "folly/Range.h"
#include <glog/logging.h>
#ifdef __SSSE3__
#include <x86intrin.h>
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 <typename T>
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<uint32_t> : public detail::GroupVarintBase<uint32_t> {
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<uint8_t>(p);
const char* end = p + detail::groupVarintLengths[k];
++p;
size_t k0 = b0key(k);
*a = loadUnaligned<uint32_t>(p) & kMask[k0];
p += k0+1;
size_t k1 = b1key(k);
*b = loadUnaligned<uint32_t>(p) & kMask[k1];
p += k1+1;
size_t k2 = b2key(k);
*c = loadUnaligned<uint32_t>(p) & kMask[k2];
p += k2+1;
size_t k3 = b3key(k);
*d = loadUnaligned<uint32_t>(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<uint64_t> : public detail::GroupVarintBase<uint64_t> {
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<uint16_t>(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<uint16_t>(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<uint16_t>(
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<uint16_t>(p);
p += 2;
uint8_t k0 = b0key(k);
*a = loadUnaligned<uint64_t>(p) & kMask[k0];
p += k0+1;
uint8_t k1 = b1key(k);
*b = loadUnaligned<uint64_t>(p) & kMask[k1];
p += k1+1;
uint8_t k2 = b2key(k);
*c = loadUnaligned<uint64_t>(p) & kMask[k2];
p += k2+1;
uint8_t k3 = b3key(k);
*d = loadUnaligned<uint64_t>(p) & kMask[k3];
p += k3+1;
uint8_t k4 = b4key(k);
*e = loadUnaligned<uint64_t>(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<uint32_t> GroupVarint32;
typedef GroupVarint<uint64_t> 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 T, class Output>
class GroupVarintEncoder {
public:
typedef GroupVarint<T> 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 <typename T>
class GroupVarintDecoder {
public:
typedef GroupVarint<T> 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<uint32_t> GroupVarint32Decoder;
typedef GroupVarintDecoder<uint64_t> GroupVarint64Decoder;
} // namespace folly
#endif /* defined(__x86_64__) || defined(__i386__) */
#endif /* FOLLY_GROUPVARINT_H_ */
-399
Ver Arquivo
@@ -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 <cstring>
#include <stdint.h>
#include <string>
#include <utility>
#include <tuple>
#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<T>; 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 <class Hasher>
inline size_t hash_combine_generic() {
return 0;
}
template <class Hasher, typename T, typename... Ts>
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<Hasher>(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 <typename T>
static size_t hash(const T& t) {
return std::hash<T>()(t);
}
};
template <typename T, typename... Ts>
size_t hash_combine(const T& t, const Ts&... ts) {
return hash_combine_generic<StdHasher>(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<const char*>(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<const char*>(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<const char*>(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<class Key>
struct hasher;
template<> struct hasher<int32_t> {
size_t operator()(int32_t key) const {
return hash::jenkins_rev_mix32(uint32_t(key));
}
};
template<> struct hasher<uint32_t> {
size_t operator()(uint32_t key) const {
return hash::jenkins_rev_mix32(key);
}
};
template<> struct hasher<int64_t> {
size_t operator()(int64_t key) const {
return hash::twang_mix64(uint64_t(key));
}
};
template<> struct hasher<uint64_t> {
size_t operator()(uint64_t key) const {
return hash::twang_mix64(key);
}
};
// recursion
template <size_t index, typename... Ts>
struct TupleHasher {
size_t operator()(std::tuple<Ts...> const& key) const {
return hash::hash_combine(
TupleHasher<index - 1, Ts...>()(key),
std::get<index>(key));
}
};
// base
template <typename... Ts>
struct TupleHasher<0, Ts...> {
size_t operator()(std::tuple<Ts...> 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 <typename T1, typename T2>
class hash<std::pair<T1, T2> > {
public:
size_t operator()(const std::pair<T1, T2>& x) const {
return folly::hash::hash_combine(x.first, x.second);
}
};
// Hash function for tuples. Requires default hash functions for all types.
template <typename... Ts>
struct hash<std::tuple<Ts...>> {
size_t operator()(std::tuple<Ts...> const& key) const {
folly::TupleHasher<
std::tuple_size<std::tuple<Ts...>>::value - 1, // start index
Ts...> hasher;
return hasher(key);
}
};
} // namespace std
#endif
-269
Ver Arquivo
@@ -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 <glog/logging.h>
namespace folly {
namespace detail {
template <typename T, typename BucketT>
HistogramBuckets<T, BucketT>::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 <typename T, typename BucketType>
unsigned int HistogramBuckets<T, BucketType>::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 <typename T, typename BucketType>
template <typename CountFn>
unsigned int HistogramBuckets<T, BucketType>::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<uint64_t> counts(numBuckets);
uint64_t totalCount = 0;
for (unsigned int n = 0; n < numBuckets; ++n) {
uint64_t bucketCount =
countFromBucket(const_cast<const BucketType&>(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<double>(curCount) / totalCount;
if (pct <= curPct) {
// This is the desired bucket
break;
}
}
if (lowPct) {
*lowPct = prevPct;
}
if (highPct) {
*highPct = curPct;
}
return idx;
}
template <typename T, typename BucketType>
template <typename CountFn, typename AvgFn>
T HistogramBuckets<T, BucketType>::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<ValueType>::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<ValueType>::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 <typename T>
std::string Histogram<T>::debugString() const {
std::string ret = folly::to<std::string>(
"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 <typename T>
void Histogram<T>::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_
-408
Ver Arquivo
@@ -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 <cstddef>
#include <limits>
#include <ostream>
#include <string>
#include <vector>
#include <stdexcept>
#include "folly/detail/Stats.h"
namespace folly {
namespace detail {
/*
* A helper class to manage a set of histogram buckets.
*/
template <typename T, typename BucketT>
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<ValueType>::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<ValueType>::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 <typename CountFn>
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 <typename CountFn, typename AvgFn>
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<BucketType>::const_iterator begin() const {
return buckets_.begin();
}
typename std::vector<BucketType>::iterator begin() {
return buckets_.begin();
}
typename std::vector<BucketType>::const_iterator end() const {
return buckets_.end();
}
typename std::vector<BucketType>::iterator end() {
return buckets_.end();
}
private:
const ValueType bucketSize_;
const ValueType min_;
const ValueType max_;
std::vector<BucketType> 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 <typename T>
class Histogram {
public:
typedef T ValueType;
typedef detail::Bucket<T> 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<int64_t>(bucket.count);
}
};
detail::HistogramBuckets<ValueType, Bucket> buckets_;
};
} // folly
#include "folly/Histogram-inl.h"
#endif // FOLLY_HISTOGRAM_H_
-135
Ver Arquivo
@@ -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 <boost/intrusive/list.hpp>
namespace folly {
/**
* An auto-unlink intrusive list hook.
*/
typedef boost::intrusive::list_member_hook<
boost::intrusive::link_mode<boost::intrusive::auto_unlink> >
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<Foo, &Foo::listHook> 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<typename T, IntrusiveListHook T::* PtrToMember>
class IntrusiveList : public boost::intrusive::list<
T,
boost::intrusive::member_hook<T, IntrusiveListHook, PtrToMember>,
boost::intrusive::constant_time_size<false> > {
};
/**
* A safe-link intrusive list hook.
*/
typedef boost::intrusive::list_member_hook<
boost::intrusive::link_mode<boost::intrusive::safe_link> >
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<Foo, &Foo::listHook> 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<typename T, SafeIntrusiveListHook T::* PtrToMember>
class CountedIntrusiveList : public boost::intrusive::list<
T,
boost::intrusive::member_hook<T, SafeIntrusiveListHook, PtrToMember>,
boost::intrusive::constant_time_size<true> > {
};
} // folly
#endif // FOLLY_INTRUSIVELIST_H_
-177
Ver Arquivo
@@ -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
-135
Ver Arquivo
@@ -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 <utility>
#include <type_traits>
#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<class Func>
struct Lazy {
typedef typename std::result_of<Func()>::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<Lazy&>(*this)();
}
result_type& operator()() {
if (!value_) value_ = func_();
return *value_;
}
private:
Optional<result_type> value_;
Func func_;
};
}
//////////////////////////////////////////////////////////////////////
template<class Func>
detail::Lazy<typename std::remove_reference<Func>::type>
lazy(Func&& fun) {
return detail::Lazy<typename std::remove_reference<Func>::type>(
std::forward<Func>(fun)
);
}
//////////////////////////////////////////////////////////////////////
}
#endif
-39
Ver Arquivo
@@ -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_ */
-46
Ver Arquivo
@@ -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 <time.h>
#include <glog/logging.h>
#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_
-285
Ver Arquivo
@@ -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 <utility>
#include <glog/logging.h>
#include "folly/detail/MPMCPipelineDetail.h"
namespace folly {
/**
* Helper tag template to use amplification > 1
*/
template <class T, size_t Amp> 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<int, std::string, int> pipeline(10, 10, 10);
*
* pipeline.blockingWrite(42);
*
* {
* int val;
* auto ticket = pipeline.blockingReadStage<0>(val);
* pipeline.blockingWriteStage<0>(ticket, folly::to<std::string>(val));
* }
*
* {
* std::string val;
* auto ticket = pipeline.blockingReadStage<1>(val);
* int ival = 0;
* try {
* ival = folly::to<int>(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<T, amplification> instead of T in the declaration:
*
* MPMCPipeline<int,
* MPMCPipelineStage<std::string, 2>,
* MPMCPipelineStage<int, 4>>
*
* 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 In, class... Stages> class MPMCPipeline {
typedef std::tuple<detail::PipelineStageInfo<Stages>...> StageInfos;
typedef std::tuple<
detail::MPMCPipelineStageImpl<In>,
detail::MPMCPipelineStageImpl<
typename detail::PipelineStageInfo<Stages>::value_type>...>
StageTuple;
static constexpr size_t kAmplification =
detail::AmplificationProduct<StageInfos>::value;
public:
/**
* Ticket, returned by blockingReadStage, must be given back to
* blockingWriteStage. Tickets are not thread-safe.
*/
template <size_t Stage>
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 <class... Sizes>
explicit MPMCPipeline(Sizes... sizes) : stages_(sizes...) { }
/**
* Push an element into (the first stage of) the pipeline. Blocking.
*/
template <class... Args>
void blockingWrite(Args&&... args) {
std::get<0>(stages_).blockingWrite(std::forward<Args>(args)...);
}
/**
* Try to push an element into (the first stage of) the pipeline.
* Non-blocking.
*/
template <class... Args>
bool write(Args&&... args) {
return std::get<0>(stages_).write(std::forward<Args>(args)...);
}
/**
* Read an element for stage Stage and obtain a ticket. Blocking.
*/
template <size_t Stage>
Ticket<Stage> blockingReadStage(
typename std::tuple_element<Stage, StageTuple>::type::value_type& elem) {
return Ticket<Stage>(
this,
std::tuple_element<Stage, StageInfos>::type::kAmplification,
std::get<Stage>(stages_).blockingRead(elem));
}
/**
* Try to read an element for stage Stage and obtain a ticket.
* Non-blocking.
*/
template <size_t Stage>
bool readStage(
Ticket<Stage>& ticket,
typename std::tuple_element<Stage, StageTuple>::type::value_type& elem) {
uint64_t tval;
if (!std::get<Stage>(stages_).readAndGetTicket(tval, elem)) {
return false;
}
ticket = Ticket<Stage>(
this,
std::tuple_element<Stage, StageInfos>::type::kAmplification,
tval);
return true;
}
/**
* Complete an element in stage Stage (pushing it for stage Stage+1).
* Blocking.
*/
template <size_t Stage, class... Args>
void blockingWriteStage(Ticket<Stage>& ticket, Args&&... args) {
std::get<Stage+1>(stages_).blockingWriteWithTicket(
ticket.use(this),
std::forward<Args>(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<sizeof...(Stages)>(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<sizeof...(Stages)>(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<sizeof...(Stages)>(stages_).readCount());
}
private:
StageTuple stages_;
};
} // namespaces
-861
Ver Arquivo
@@ -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 <algorithm>
#include <atomic>
#include <assert.h>
#include <boost/noncopyable.hpp>
#include <errno.h>
#include <limits>
#include <linux/futex.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <folly/Traits.h>
#include <folly/detail/Futex.h>
namespace folly {
namespace detail {
template<typename T, template<typename> class Atom>
class SingleElementQueue;
template <typename T> class MPMCPipelineStageImpl;
} // namespace detail
/// MPMCQueue<T> 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<typename T,
template<typename> class Atom = std::atomic,
typename = typename std::enable_if<
std::is_nothrow_constructible<T,T&&>::value ||
folly::IsRelocatable<T>::value>::type>
class MPMCQueue : boost::noncopyable {
friend class detail::MPMCPipelineStageImpl<T>;
public:
typedef T value_type;
explicit MPMCQueue(size_t capacity)
: capacity_(capacity)
, slots_(new detail::SingleElementQueue<T,Atom>[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<T,Atom>) >= 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<T,Atom>&& 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<T,Atom> const& operator= (MPMCQueue<T,Atom>&& 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<ssize_t>(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 <typename ...Args>
void blockingWrite(Args&&... args) noexcept {
enqueueWithTicket(pushTicket_++, std::forward<Args>(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 <typename ...Args>
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>(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 <typename ...Args>
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>(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<T,Atom>)
};
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<T,Atom>* 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<uint64_t> pushTicket_ FOLLY_ON_NEXT_CACHE_LINE;
/// Dequeuers get tickets from here
Atom<uint64_t> 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<int> pushSpinCutoff_ FOLLY_ON_NEXT_CACHE_LINE;
/// The adaptive spin cutoff when the queue is empty on dequeue
Atom<int> 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<int>)];
#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<ssize_t>(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 <typename ...Args>
void enqueueWithTicket(uint64_t ticket, Args&&... args) noexcept {
slots_[idx(ticket)].enqueue(turn(ticket),
pushSpinCutoff_,
(ticket % kAdaptationFreq) == 0,
std::forward<Args>(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 <template<typename> 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<int>& 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<uint32_t>::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<int>::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<Atom> 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 <typename T, template <typename> 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 <typename ...Args,
typename = typename std::enable_if<
std::is_nothrow_constructible<T,Args...>::value>::type>
void enqueue(const uint32_t turn,
Atom<int>& spinCutoff,
const bool updateSpinCutoff,
Args&&... args) noexcept {
sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff);
new (contents_) T(std::forward<Args>(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 <typename = typename std::enable_if<
(folly::IsRelocatable<T>::value &&
boost::has_nothrow_constructor<T>::value) ||
std::is_nothrow_constructible<T,T&&>::value>::type>
void enqueue(const uint32_t turn,
Atom<int>& spinCutoff,
const bool updateSpinCutoff,
T&& goner) noexcept {
if (std::is_nothrow_constructible<T,T&&>::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<int>& spinCutoff,
const bool updateSpinCutoff,
T& elem) noexcept {
if (folly::IsRelocatable<T>::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<Atom> sequencer_;
T* ptr() noexcept {
return static_cast<T*>(static_cast<void*>(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
-229
Ver Arquivo
@@ -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 <string>
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 <malloc.h>
#else
#include <stdlib.h>
#endif
#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <new>
#include <bits/functexcept.h>
/**
* 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_
-72
Ver Arquivo
@@ -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 <class Map>
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 <class Map>
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 <class Map>
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 <class Map>
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_ */
-346
Ver Arquivo
@@ -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 <memory>
#include <limits>
#include <utility>
#include <exception>
#include <stdexcept>
#include <cstddef>
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 T, typename Dp = std::default_delete<T>, typename... Args>
std::unique_ptr<T, Dp> make_unique(Args&&... args) {
return std::unique_ptr<T, Dp>(new T(std::forward<Args>(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<MallocAllocator,...> to a
* standard container it will be larger due to the contained state
* pointer.
*
* author: Tudor Bosman <tudorb@fb.com>
*/
// This would be so much simpler with std::allocator_traits, but gcc 4.6.2
// doesn't support it.
template <class Alloc, class T> class StlAllocator;
template <class Alloc> class StlAllocator<Alloc, void> {
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 <class U> struct rebind {
typedef StlAllocator<Alloc, U> other;
};
bool operator!=(const StlAllocator<Alloc, void>& other) const {
return alloc_ != other.alloc_;
}
bool operator==(const StlAllocator<Alloc, void>& other) const {
return alloc_ == other.alloc_;
}
private:
Alloc* alloc_;
};
template <class Alloc, class T>
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 <class U> StlAllocator(const StlAllocator<Alloc, U>& other)
: alloc_(other.alloc()) { }
T* allocate(size_t n, const void* hint = nullptr) {
return static_cast<T*>(alloc_->allocate(n * sizeof(T)));
}
void deallocate(T* p, size_t n) {
alloc_->deallocate(p);
}
size_t max_size() const {
return std::numeric_limits<size_t>::max();
}
T* address(T& x) const {
return std::addressof(x);
}
const T* address(const T& x) const {
return std::addressof(x);
}
template <class... Args>
void construct(T* p, Args&&... args) {
new (p) T(std::forward<Args>(args)...);
}
void destroy(T* p) {
p->~T();
}
Alloc* alloc() const {
return alloc_;
}
template <class U> struct rebind {
typedef StlAllocator<Alloc, U> other;
};
bool operator!=(const StlAllocator<Alloc, T>& other) const {
return alloc_ != other.alloc_;
}
bool operator==(const StlAllocator<Alloc, T>& other) const {
return alloc_ == other.alloc_;
}
private:
Alloc* alloc_;
};
/**
* Helper function to obtain rebound allocators
*
* @author: Marcelo Juchem <marcelo@fb.com>
*/
template <typename T, typename Allocator>
typename Allocator::template rebind<T>::other rebind_allocator(
Allocator const& allocator
) {
return typename Allocator::template rebind<T>::other(allocator);
}
/*
* Helper classes/functions for creating a unique_ptr using a custom
* allocator.
*
* @author: Marcelo Juchem <marcelo@fb.com>
*/
// Derives from the allocator to take advantage of the empty base
// optimization when possible.
template <typename Allocator>
class allocator_delete
: private std::remove_reference<Allocator>::type
{
typedef typename std::remove_reference<Allocator>::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 <typename U>
allocator_delete(const allocator_delete<U>& other)
: allocator_type(other.get_allocator())
{}
allocator_type& get_allocator() const {
return *const_cast<allocator_delete*>(this);
}
void operator()(pointer p) const {
if (!p) return;
const_cast<allocator_delete*>(this)->destroy(p);
const_cast<allocator_delete*>(this)->deallocate(p, 1);
}
};
template <typename T, typename Allocator>
class is_simple_allocator {
FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy);
typedef typename std::remove_const<
typename std::remove_reference<Allocator>::type
>::type allocator;
typedef typename std::remove_reference<T>::type value_type;
typedef value_type* pointer;
public:
constexpr static bool value = !has_destroy<allocator, void(pointer)>::value
&& !has_destroy<allocator, void(void*)>::value;
};
template <typename T, typename Allocator>
struct as_stl_allocator {
typedef typename std::conditional<
is_simple_allocator<T, Allocator>::value,
folly::StlAllocator<
typename std::remove_reference<Allocator>::type,
typename std::remove_reference<T>::type
>,
typename std::remove_reference<Allocator>::type
>::type type;
};
template <typename T, typename Allocator>
typename std::enable_if<
is_simple_allocator<T, Allocator>::value,
folly::StlAllocator<
typename std::remove_reference<Allocator>::type,
typename std::remove_reference<T>::type
>
>::type make_stl_allocator(Allocator&& allocator) {
return folly::StlAllocator<
typename std::remove_reference<Allocator>::type,
typename std::remove_reference<T>::type
>(&allocator);
}
template <typename T, typename Allocator>
typename std::enable_if<
!is_simple_allocator<T, Allocator>::value,
typename std::remove_reference<Allocator>::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 <marcelo@fb.com>
*/
template <typename T, typename Allocator>
struct AllocatorUniquePtr {
typedef std::unique_ptr<T,
folly::allocator_delete<
typename std::conditional<
is_simple_allocator<T, Allocator>::value,
folly::StlAllocator<typename std::remove_reference<Allocator>::type, T>,
typename std::remove_reference<Allocator>::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 <marcelo@fb.com>
*/
template <typename T, typename Allocator, typename ...Args>
typename AllocatorUniquePtr<T, Allocator>::type allocate_unique(
Allocator&& allocator, Args&&... args
) {
auto stlAllocator = folly::make_stl_allocator<T>(
std::forward<Allocator>(allocator)
);
auto p = stlAllocator.allocate(1);
try {
stlAllocator.construct(p, std::forward<Args>(args)...);
return {p,
folly::allocator_delete<decltype(stlAllocator)>(std::move(stlAllocator))
};
} catch (...) {
stlAllocator.deallocate(p, 1);
throw;
}
}
template <typename T, typename Allocator, typename ...Args>
std::shared_ptr<T> allocate_shared(Allocator&& allocator, Args&&... args) {
return std::allocate_shared<T>(
folly::make_stl_allocator<T>(std::forward<Allocator>(allocator)),
std::forward<Args>(args)...
);
}
} // namespace folly
#endif /* FOLLY_MEMORY_H_ */
-215
Ver Arquivo
@@ -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 <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <system_error>
#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<unsigned char*>(
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<int(void*, size_t)> 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<char*>(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
-163
Ver Arquivo
@@ -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 <glog/logging.h>
#include <boost/noncopyable.hpp>
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<class T>
Range<const T*> asRange() const {
size_t count = data_.size() / sizeof(T);
return Range<const T*>(static_cast<const T*>(
static_cast<const void*>(data_.data())),
count);
}
/**
* A range of bytes mapped by this mapping.
*/
Range<const uint8_t*> range() const {
return {data_.begin(), data_.end()};
}
/**
* Return the memory area where the file was mapped.
*/
StringPiece data() const {
return asRange<const char>();
}
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<uint8_t*> 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<class T>
Range<T*> asWritableRange() const {
size_t count = data_.size() / sizeof(T);
return Range<T*>(static_cast<T*>(
static_cast<void*>(data_.data())),
count);
}
/**
* A range of mutable bytes mapped by this mapping.
*/
Range<uint8_t*> writableRange() const {
return data_;
}
};
} // namespace folly
#endif /* FOLLY_MEMORYMAPPING_H_ */
-310
Ver Arquivo
@@ -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<Logger> 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<Iterator>& it,
* initializer_list<int> idsExpected,
* Optional<initializer_list<int>> 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<int> maybeInt = ...;
* if (int* v = get_pointer(maybeInt)) {
* cout << *v << endl;
* }
*/
#include <utility>
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <boost/operators.hpp>
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 Value>
class Optional {
public:
static_assert(!std::is_reference<Value>::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<class Arg>
Optional& operator=(Arg&& arg) {
assign(std::forward<Arg>(arg));
return *this;
}
Optional& operator=(Optional &&other) {
assign(std::move(other));
return *this;
}
Optional& operator=(const Optional &other) {
assign(other);
return *this;
}
template<class... Args>
void emplace(Args&&... args) {
clear();
construct(std::forward<Args>(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<class... Args>
void construct(Args&&... args) {
const void* ptr = &value_;
// for supporting const types
new(const_cast<void*>(ptr)) Value(std::forward<Args>(args)...);
hasValue_ = true;
}
// uninitialized
union { Value value_; };
bool hasValue_;
};
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
template<class T>
const T* get_pointer(const Optional<T>& opt) {
return opt ? &opt.value() : nullptr;
}
template<class T>
T* get_pointer(Optional<T>& opt) {
return opt ? &opt.value() : nullptr;
}
template<class T>
void swap(Optional<T>& a, Optional<T>& 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<class T,
class Opt = Optional<typename std::decay<T>::type>>
Opt make_optional(T&& v) {
return Opt(std::forward<T>(v));
}
template<class V>
bool operator< (const Optional<V>& a, const Optional<V>& b) {
if (a.hasValue() != b.hasValue()) { return a.hasValue() < b.hasValue(); }
if (a.hasValue()) { return a.value() < b.value(); }
return false;
}
template<class V>
bool operator==(const Optional<V>& a, const Optional<V>& b) {
if (a.hasValue() != b.hasValue()) { return false; }
if (a.hasValue()) { return a.value() == b.value(); }
return true;
}
template<class V>
bool operator<=(const Optional<V>& a, const Optional<V>& b) {
return !(b < a);
}
template<class V>
bool operator!=(const Optional<V>& a, const Optional<V>& b) {
return !(b == a);
}
template<class V>
bool operator>=(const Optional<V>& a, const Optional<V>& b) {
return !(a < b);
}
template<class V>
bool operator> (const Optional<V>& a, const Optional<V>& b) {
return b < a;
}
// To supress comparability of Optional<T> with T, despite implicit conversion.
template<class V> bool operator< (const Optional<V>&, const V& other) = delete;
template<class V> bool operator<=(const Optional<V>&, const V& other) = delete;
template<class V> bool operator==(const Optional<V>&, const V& other) = delete;
template<class V> bool operator!=(const Optional<V>&, const V& other) = delete;
template<class V> bool operator>=(const Optional<V>&, const V& other) = delete;
template<class V> bool operator> (const Optional<V>&, const V& other) = delete;
template<class V> bool operator< (const V& other, const Optional<V>&) = delete;
template<class V> bool operator<=(const V& other, const Optional<V>&) = delete;
template<class V> bool operator==(const V& other, const Optional<V>&) = delete;
template<class V> bool operator!=(const V& other, const Optional<V>&) = delete;
template<class V> bool operator>=(const V& other, const Optional<V>&) = delete;
template<class V> bool operator> (const V& other, const Optional<V>&) = delete;
} // namespace folly
#endif//FOLLY_OPTIONAL_H_
-150
Ver Arquivo
@@ -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 <sahrens@fb.com>
* @author Jordan DeLong <delong.j@fb.com>
*/
#include "folly/SmallLocks.h"
#include <type_traits>
#include <glog/logging.h>
namespace folly {
template<class T>
class PackedSyncPtr {
// This just allows using this class even with T=void. Attempting
// to use the operator* or operator[] on a PackedSyncPtr<void> will
// still properly result in a compile error.
typedef typename std::add_lvalue_reference<T>::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<uintptr_t>(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<uintptr_t>(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<T*>(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<T> 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<uintptr_t> data_;
};
static_assert(sizeof(PackedSyncPtr<void>) == 8,
"PackedSyncPtr should be only 8 bytes---something is "
"messed up");
}
#endif
-506
Ver Arquivo
@@ -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 <cassert>
#include <cstdint>
#include <cstring>
#include <functional>
#include <iterator>
#include <limits>
#include <type_traits>
#include <boost/iterator/iterator_adaptor.hpp>
#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 T, size_t NS, class Enable=void>
class Node;
namespace detail {
// Shortcut to avoid writing the long enable_if expression every time
template <class T, size_t NS, class Enable=void> struct NodeValid;
template <class T, size_t NS>
struct NodeValid<T, NS,
typename std::enable_if<(
std::is_trivial<T>::value &&
sizeof(T) <= NS &&
NS % alignof(T) == 0)>::type> {
typedef void type;
};
} // namespace detail
template <class T, size_t NS>
class Node<T, NS, typename detail::NodeValid<T,NS>::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 <class T, size_t NS> constexpr size_t
Node<T, NS, typename detail::NodeValid<T,NS>::type>::kNodeSize;
template <class T, size_t NS> constexpr size_t
Node<T, NS, typename detail::NodeValid<T,NS>::type>::kElementCount;
template <class T, size_t NS> constexpr size_t
Node<T, NS, typename detail::NodeValid<T,NS>::type>::kPaddingBytes;
template <class Iter> class Iterator;
namespace detail {
// Helper class to transfer the constness from From (a lvalue reference)
// and create a lvalue reference to To.
//
// TransferReferenceConstness<const string&, int> -> const int&
// TransferReferenceConstness<string&, int> -> int&
// TransferReferenceConstness<string&, const int> -> const int&
template <class From, class To, class Enable=void>
struct TransferReferenceConstness;
template <class From, class To>
struct TransferReferenceConstness<
From, To, typename std::enable_if<std::is_const<
typename std::remove_reference<From>::type>::value>::type> {
typedef typename std::add_lvalue_reference<
typename std::add_const<To>::type>::type type;
};
template <class From, class To>
struct TransferReferenceConstness<
From, To, typename std::enable_if<!std::is_const<
typename std::remove_reference<From>::type>::value>::type> {
typedef typename std::add_lvalue_reference<To>::type type;
};
// Helper class template to define a base class for Iterator (below) and save
// typing.
template <class Iter>
struct IteratorBase {
typedef boost::iterator_adaptor<
// CRTC
Iterator<Iter>,
// Base iterator type
Iter,
// Value type
typename std::iterator_traits<Iter>::value_type::value_type,
// Category or traversal
boost::use_default,
// Reference type
typename detail::TransferReferenceConstness<
typename std::iterator_traits<Iter>::reference,
typename std::iterator_traits<Iter>::value_type::value_type
>::type
> type;
};
} // namespace detail
/**
* Wrapper around iterators to Node to return iterators to the underlying
* node elements.
*/
template <class Iter>
class Iterator : public detail::IteratorBase<Iter>::type {
typedef typename detail::IteratorBase<Iter>::type Super;
public:
typedef typename std::iterator_traits<Iter>::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 <class Container>
Iterator<typename Container::const_iterator> cbegin(const Container& c) {
return Iterator<typename Container::const_iterator>(std::begin(c));
}
template <class Container>
Iterator<typename Container::const_iterator> cend(const Container& c) {
return Iterator<typename Container::const_iterator>(std::end(c));
}
template <class Container>
Iterator<typename Container::const_iterator> begin(const Container& c) {
return cbegin(c);
}
template <class Container>
Iterator<typename Container::const_iterator> end(const Container& c) {
return cend(c);
}
template <class Container>
Iterator<typename Container::iterator> begin(Container& c) {
return Iterator<typename Container::iterator>(std::begin(c));
}
template <class Container>
Iterator<typename Container::iterator> end(Container& c) {
return Iterator<typename Container::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 Container>
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<typename Container::iterator> iterator;
typedef Iterator<typename Container::const_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<size_type>::max() /
Node::kElementCount) ?
c_.max_size() * Node::kElementCount :
std::numeric_limits<size_type>::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<Container, size_t> move() {
std::pair<Container, size_t> 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<const Container&, size_t> 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_ */
-87
Ver Arquivo
@@ -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 <features.h>
#endif
#ifdef FOLLY_HAVE_SCHED_H
#include <sched.h>
#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_
-70
Ver Arquivo
@@ -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_
-177
Ver Arquivo
@@ -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 <atomic>
#include <cassert>
#include <cstdlib>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <boost/noncopyable.hpp>
#include <boost/type_traits.hpp>
namespace folly {
/*
* ProducerConsumerQueue is a one producer and one consumer queue
* without locks.
*/
template<class T>
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<T*>(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<T>::value) {
int read = readIndex_;
int end = writeIndex_;
while (read != end) {
records_[read].~T();
if (++read == size_) {
read = 0;
}
}
}
std::free(records_);
}
template<class ...Args>
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<Args>(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<int> readIndex_;
std::atomic<int> writeIndex_;
};
}
#endif
-743
Ver Arquivo
@@ -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 <xliux@fb.com>
*/
#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 <x86intrin.h>
#else
#undef RW_SPINLOCK_USE_X86_INTRINSIC_
#endif
#include <atomic>
#include <string>
#include <algorithm>
#include <boost/noncopyable.hpp>
#include <sched.h>
#include <glog/logging.h>
#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<int32_t> bits_;
};
#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
// A more balanced Read-Write spin lock implemented based on GCC intrinsics.
namespace detail {
template <size_t kBitWidth> 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<size_t kBitWidth, bool kFavorWriter=false>
class RWTicketSpinLockT : boost::noncopyable {
typedef detail::RWTicketIntTrait<kBitWidth> IntTraitType;
typedef typename detail::RWTicketIntTrait<kBitWidth>::FullInt FullInt;
typedef typename detail::RWTicketIntTrait<kBitWidth>::HalfInt HalfInt;
typedef typename detail::RWTicketIntTrait<kBitWidth>::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<class T> static T load_acquire(T* addr) {
T t = *addr; // acquire barrier
asm volatile("" : : : "memory");
return t;
}
template<class T>
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<kBitWidth, kFavorWriter> 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_
-42
Ver Arquivo
@@ -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 <atomic>
#include <unistd.h>
#include <sys/time.h>
namespace folly {
namespace {
std::atomic<uint32_t> 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<uint32_t>(getpid())
+ kPrime2 * static_cast<uint32_t>(tv.tv_sec)
+ kPrime3 * static_cast<uint32_t>(tv.tv_usec);
}
}
-31
Ver Arquivo
@@ -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 <stdint.h>
namespace folly {
/*
* Return a good seed for a random number generator.
*/
uint32_t randomNumberSeed();
}
#endif
-279
Ver Arquivo
@@ -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 <emmintrin.h> // __v16qi
#include <iostream>
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<const char*>(ptr) - haystack.data();
best = std::min<size_t>(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<uintptr_t>(addr) / kMinPageSize)
#if FOLLY_HAVE_EMMINTRIN_H
inline size_t nextAlignedIndex(const char* arr) {
auto firstPossible = reinterpret_cast<uintptr_t>(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<const __v16qi*>(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 <bool HAYSTACK_ALIGNED>
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 <bool HAYSTACK_ALIGNED>
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<const __v16qi*>(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<const __v16qi*>(ptr2);
auto index = __builtin_ia32_pcmpestri128(
arr2, needles.size() - j, arr1, haystack.size() - blockStartIdx, 0);
b = std::min<size_t>(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<false>(haystack, needles, 0);
if (ret != StringPiece::npos) {
return ret;
}
size_t i = nextAlignedIndex(haystack.data());
for (; i < haystack.size(); i += 16) {
auto ret = scanHaystackBlock<true>(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
-757
Ver Arquivo
@@ -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 <glog/logging.h>
#include <algorithm>
#include <cstring>
#include <iosfwd>
#include <string>
#include <stdexcept>
#include <type_traits>
#include <boost/operators.hpp>
#include <bits/c++config.h>
#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 T> 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 <class T, class Comp = std::equal_to<typename Range<T>::value_type>>
inline size_t qfind(const Range<T> & haystack,
const Range<T> & 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 <class T>
size_t qfind(const Range<T> & haystack,
const typename Range<T>::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 <class T>
size_t rfind(const Range<T> & haystack,
const typename Range<T>::value_type& needle);
/**
* Finds the first occurrence of any element of needle in
* haystack. The algorithm is O(haystack.size() * needle.size()).
*/
template <class T>
inline size_t qfind_first_of(const Range<T> & haystack,
const Range<T> & 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 <class Iter>
typename std::enable_if<
std::is_same<typename std::iterator_traits<Iter>::iterator_category,
std::random_access_iterator_tag>::value,
typename std::iterator_traits<Iter>::reference>::type
value_before(Iter i) {
return i[-1];
}
/**
* For all other iterators, we need to use the decrement operator.
*/
template <class Iter>
typename std::enable_if<
!std::is_same<typename std::iterator_traits<Iter>::iterator_category,
std::random_access_iterator_tag>::value,
typename std::iterator_traits<Iter>::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 Iter>
class Range : private boost::totally_ordered<Range<Iter> > {
public:
typedef std::size_t size_type;
typedef Iter iterator;
typedef Iter const_iterator;
typedef typename std::remove_reference<
typename std::iterator_traits<Iter>::reference>::type
value_type;
typedef typename std::iterator_traits<Iter>::reference reference;
typedef std::char_traits<typename std::remove_const<value_type>::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<const char*>
/* implicit */ Range(Iter str)
: b_(str), e_(b_ + strlen(str)) {}
// Works only for Range<const char*>
/* implicit */ Range(const std::string& str)
: b_(str.data()), e_(b_ + str.size()) {}
// Works only for Range<const char*>
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<const char*>
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<Iter>& 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<const char*>
/* implicit */ Range(const fbstring& str)
: b_(str.data()), e_(b_ + str.size()) { }
// Works only for Range<const char*>
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<const char*>
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<const char*> (aka StringPiece) to
// Range<const unsigned char*> (aka ByteRange), as they're both frequently
// used to represent ranges of bytes. Allow explicit conversion in the other
// direction.
template <class OtherIter, typename std::enable_if<
(std::is_same<Iter, const unsigned char*>::value &&
std::is_same<OtherIter, const char*>::value), int>::type = 0>
/* implicit */ Range(const Range<OtherIter>& other)
: b_(reinterpret_cast<const unsigned char*>(other.begin())),
e_(reinterpret_cast<const unsigned char*>(other.end())) {
}
template <class OtherIter, typename std::enable_if<
(std::is_same<Iter, const char*>::value &&
std::is_same<OtherIter, const unsigned char*>::value), int>::type = 0>
explicit Range(const Range<OtherIter>& other)
: b_(reinterpret_cast<const char*>(other.begin())),
e_(reinterpret_cast<const char*>(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<const char*>
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<const char*>
std::string str() const { return std::string(b_, size()); }
std::string toString() const { return str(); }
// Works only for Range<const char*>
fbstring fbstr() const { return fbstring(b_, size()); }
fbstring toFbstring() const { return fbstr(); }
// Works only for Range<const char*>
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<const char*>
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<std::string::size_type>(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<const (unsigned) char*> which have Range(Iter) ctor
size_type find(const Iter s) const {
return qfind(*this, Range(s));
}
// Works only for Range<const (unsigned) char*> 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<const (unsigned) char*> which have Range(Iter) ctor
size_type find_first_of(Iter needles) const {
return find_first_of(Range(needles));
}
// Works only for Range<const (unsigned) char*> 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 <class Iter>
const typename Range<Iter>::size_type Range<Iter>::npos = std::string::npos;
template <class T>
void swap(Range<T>& lhs, Range<T>& rhs) {
lhs.swap(rhs);
}
/**
* Create a range from two iterators, with type deduction.
*/
template <class Iter>
Range<Iter> makeRange(Iter first, Iter last) {
return Range<Iter>(first, last);
}
typedef Range<const char*> StringPiece;
typedef Range<const unsigned char*> ByteRange;
std::ostream& operator<<(std::ostream& os, const StringPiece& piece);
/**
* Templated comparison operators
*/
template <class T>
inline bool operator==(const Range<T>& lhs, const Range<T>& rhs) {
return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
}
template <class T>
inline bool operator<(const Range<T>& lhs, const Range<T>& rhs) {
return lhs.compare(rhs) < 0;
}
/**
* Specializations of comparison operators for StringPiece
*/
namespace detail {
template <class A, class B>
struct ComparableAsStringPiece {
enum {
value =
(std::is_convertible<A, StringPiece>::value
&& std::is_same<B, StringPiece>::value)
||
(std::is_convertible<B, StringPiece>::value
&& std::is_same<A, StringPiece>::value)
};
};
} // namespace detail
/**
* operator== through conversion for Range<const char*>
*/
template <class T, class U>
typename
std::enable_if<detail::ComparableAsStringPiece<T, U>::value, bool>::type
operator==(const T& lhs, const U& rhs) {
return StringPiece(lhs) == StringPiece(rhs);
}
/**
* operator< through conversion for Range<const char*>
*/
template <class T, class U>
typename
std::enable_if<detail::ComparableAsStringPiece<T, U>::value, bool>::type
operator<(const T& lhs, const U& rhs) {
return StringPiece(lhs) < StringPiece(rhs);
}
/**
* operator> through conversion for Range<const char*>
*/
template <class T, class U>
typename
std::enable_if<detail::ComparableAsStringPiece<T, U>::value, bool>::type
operator>(const T& lhs, const U& rhs) {
return StringPiece(lhs) > StringPiece(rhs);
}
/**
* operator< through conversion for Range<const char*>
*/
template <class T, class U>
typename
std::enable_if<detail::ComparableAsStringPiece<T, U>::value, bool>::type
operator<=(const T& lhs, const U& rhs) {
return StringPiece(lhs) <= StringPiece(rhs);
}
/**
* operator> through conversion for Range<const char*>
*/
template <class T, class U>
typename
std::enable_if<detail::ComparableAsStringPiece<T, U>::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<std::size_t>(str.hash());
}
};
/**
* Finds substrings faster than brute force by borrowing from Boyer-Moore
*/
template <class T, class Comp>
size_t qfind(const Range<T>& haystack,
const Range<T>& 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 <class T, class Comp>
size_t qfind_first_of(const Range<T> & haystack,
const Range<T> & 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 <class T>
size_t qfind(const Range<T>& haystack,
const typename Range<T>::value_type& needle) {
auto pos = std::find(haystack.begin(), haystack.end(), needle);
return pos == haystack.end() ? std::string::npos : pos - haystack.data();
}
template <class T>
size_t rfind(const Range<T>& haystack,
const typename Range<T>::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<const char*>& haystack, const char& needle) {
auto pos = static_cast<const char*>(
::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<const char*>& haystack, const char& needle) {
auto pos = static_cast<const char*>(
::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<const unsigned char*>& haystack,
const unsigned char& needle) {
auto pos = static_cast<const unsigned char*>(
::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<const unsigned char*>& haystack,
const unsigned char& needle) {
auto pos = static_cast<const unsigned char*>(
::memrchr(haystack.data(), needle, haystack.size()));
return pos == nullptr ? std::string::npos : pos - haystack.data();
}
#endif
template <class T>
size_t qfind_first_of(const Range<T>& haystack,
const Range<T>& needles) {
return qfind_first_of(haystack, needles, asciiCaseSensitive);
}
// specialization for StringPiece
template <>
inline size_t qfind_first_of(const Range<const char*>& haystack,
const Range<const char*>& needles) {
return detail::qfind_first_byte_of(haystack, needles);
}
// specialization for ByteRange
template <>
inline size_t qfind_first_of(const Range<const unsigned char*>& haystack,
const Range<const unsigned char*>& 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_
-147
Ver Arquivo
@@ -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 <cstddef>
#include <functional>
#include <new>
#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<void()>,
* 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<typename FunctionType>
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<typename FunctionType>
ScopeGuardImpl<typename std::decay<FunctionType>::type>
makeGuard(FunctionType&& fn) {
return ScopeGuardImpl<typename std::decay<FunctionType>::type>(
std::forward<FunctionType>(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 <typename FunctionType>
ScopeGuardImpl<typename std::decay<FunctionType>::type>
operator+(detail::ScopeGuardOnExit, FunctionType&& fn) {
return ScopeGuardImpl<typename std::decay<FunctionType>::type>(
std::forward<FunctionType>(fn));
}
} // namespace detail
} // folly
#define SCOPE_EXIT \
auto FB_ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
= ::folly::detail::ScopeGuardOnExit() + [&]
#endif // FOLLY_SCOPEGUARD_H_
-331
Ver Arquivo
@@ -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 <kma@fb.com>
* @author Jordan DeLong <delong.j@fb.com>
*/
#include <array>
#include <cinttypes>
#include <type_traits>
#include <ctime>
#include <boost/noncopyable.hpp>
#include <cstdlib>
#include <pthread.h>
#include <mutex>
#include <glog/logging.h>
#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<class IntType, int Bit = sizeof(IntType) * 8 - 1>
struct PicoSpinLock {
// Internally we deal with the unsigned version of the type.
typedef typename std::make_unsigned<IntType>::type UIntType;
static_assert(std::is_integral<IntType>::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<IntType>(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 <class T, size_t N>
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<PaddedSpinLock, N> data_;
} __attribute__((aligned));
//////////////////////////////////////////////////////////////////////
typedef std::lock_guard<MicroSpinLock> MSLGuard;
//////////////////////////////////////////////////////////////////////
}
#endif
-374
Ver Arquivo
@@ -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 <cstring>
#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
-304
Ver Arquivo
@@ -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 <cstddef>
#include <cstdint>
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
-374
Ver Arquivo
@@ -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 <cstring>
#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
-304
Ver Arquivo
@@ -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 <cstddef>
#include <cstdint>
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
-373
Ver Arquivo
@@ -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 <cstring>
#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
-309
Ver Arquivo
@@ -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 <cstddef>
#include <cstdint>
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
-267
Ver Arquivo
@@ -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 <memory>
#include <limits>
#include <utility>
#include <exception>
#include <stdexcept>
#include <cstddef>
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 <tudorb@fb.com>
*/
// This would be so much simpler with std::allocator_traits, but gcc 4.6.2
// doesn't support it
template <class Alloc, class T> class StlAllocator;
template <class Alloc> class StlAllocator<Alloc, void> {
public:
typedef void value_type;
typedef void* pointer;
typedef const void* const_pointer;
template <class U> struct rebind {
typedef StlAllocator<Alloc, U> other;
};
};
template <class Alloc, class T>
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 <class U> StlAllocator(const StlAllocator<Alloc, U>& other)
: alloc_(other.alloc()) { }
T* allocate(size_t n, const void* hint = nullptr) {
return static_cast<T*>(alloc_->allocate(n * sizeof(T)));
}
void deallocate(T* p, size_t n) {
alloc_->deallocate(p);
}
size_t max_size() const {
return std::numeric_limits<size_t>::max();
}
T* address(T& x) const {
return std::addressof(x);
}
const T* address(const T& x) const {
return std::addressof(x);
}
template <class... Args>
void construct(T* p, Args&&... args) {
new (p) T(std::forward<Args>(args)...);
}
void destroy(T* p) {
p->~T();
}
Alloc* alloc() const {
return alloc_;
}
template <class U> struct rebind {
typedef StlAllocator<Alloc, U> other;
};
bool operator!=(const StlAllocator<Alloc, T>& other) const {
return alloc_ != other.alloc_;
}
bool operator==(const StlAllocator<Alloc, T>& other) const {
return alloc_ == other.alloc_;
}
private:
Alloc* alloc_;
};
/*
* Helper classes/functions for creating a unique_ptr using a custom allocator
*
* @author: Marcelo Juchem <marcelo@fb.com>
*/
// A deleter implementation based on std::default_delete,
// which uses a custom allocator to free memory
template <typename Allocator>
class allocator_delete {
typedef typename std::remove_reference<Allocator>::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 <typename U>
allocator_delete(const allocator_delete<U>& 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 <typename T, typename Allocator>
class is_simple_allocator {
FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy);
typedef typename std::remove_const<
typename std::remove_reference<Allocator>::type
>::type allocator;
typedef typename std::remove_reference<T>::type value_type;
typedef value_type* pointer;
public:
constexpr static bool value = !has_destroy<allocator, void(pointer)>::value
&& !has_destroy<allocator, void(void*)>::value;
};
template <typename T, typename Allocator>
typename std::enable_if<
is_simple_allocator<T, Allocator>::value,
folly::StlAllocator<
typename std::remove_reference<Allocator>::type,
typename std::remove_reference<T>::type
>
>::type make_stl_allocator(Allocator&& allocator) {
return folly::StlAllocator<
typename std::remove_reference<Allocator>::type,
typename std::remove_reference<T>::type
>(&allocator);
}
template <typename T, typename Allocator>
typename std::enable_if<
!is_simple_allocator<T, Allocator>::value,
typename std::remove_reference<Allocator>::type
>::type make_stl_allocator(Allocator&& allocator) {
return std::move(allocator);
}
template <typename T, typename Allocator>
struct AllocatorUniquePtr {
typedef std::unique_ptr<T,
folly::allocator_delete<
typename std::conditional<
is_simple_allocator<T, Allocator>::value,
folly::StlAllocator<typename std::remove_reference<Allocator>::type, T>,
typename std::remove_reference<Allocator>::type
>::type
>
> type;
};
template <typename T, typename Allocator, typename ...Args>
typename AllocatorUniquePtr<T, Allocator>::type allocate_unique(
Allocator&& allocator, Args&&... args
) {
auto stlAllocator = folly::make_stl_allocator<T>(
std::forward<Allocator>(allocator)
);
auto p = stlAllocator.allocate(1);
try {
stlAllocator.construct(p, std::forward<Args>(args)...);
return {p,
folly::allocator_delete<decltype(stlAllocator)>(std::move(stlAllocator))
};
} catch (...) {
stlAllocator.deallocate(p, 1);
throw;
}
}
template <typename T, typename Allocator, typename ...Args>
std::shared_ptr<T> allocate_shared(Allocator&& allocator, Args&&... args) {
return std::allocate_shared<T>(
folly::make_stl_allocator<T>(std::forward<Allocator>(allocator)),
std::forward<Args>(args)...
);
}
} // namespace folly
#endif /* FOLLY_STLALLOCATOR_H_ */
-651
Ver Arquivo
@@ -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 <stdexcept>
#include <iterator>
#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 <class String>
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<unsigned char>(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 <class String>
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<unsigned char>(*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<unsigned char>(*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 <class String>
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<unsigned char>(mode);
while (p != str.end()) {
char c = *p;
unsigned char v = static_cast<unsigned char>(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 <class String>
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<unsigned char>(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<unsigned char>(p[1])];
auto h2 = detail::hexTable[static_cast<unsigned char>(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<class OutStringT> struct OutputConverter {};
template<> struct OutputConverter<std::string> {
std::string operator()(StringPiece sp) const {
return sp.toString();
}
};
template<> struct OutputConverter<fbstring> {
fbstring operator()(StringPiece sp) const {
return sp.toFbstring();
}
};
template<> struct OutputConverter<StringPiece> {
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<class OutStringT, class DelimT, class OutputIterator>
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<OutStringT> conv;
if (dSize > strSize || dSize == 0) {
if (!ignoreEmpty || strSize > 0) {
*out++ = conv(sp);
}
return;
}
if (boost::is_same<DelimT,StringPiece>::value && dSize == 1) {
// Call the char version because it is significantly faster.
return internalSplit<OutStringT>(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<class String> StringPiece prepareDelim(const String& s) {
return StringPiece(s);
}
inline char prepareDelim(char c) { return c; }
template<bool exact,
class Delim>
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 exact,
class Delim,
class... StringPieces>
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<exact>(delimiter, tail, outTail...))) {
outHead = head;
return true;
}
return false;
}
}
//////////////////////////////////////////////////////////////////////
template<class Delim, class String, class OutputType>
void split(const Delim& delimiter,
const String& input,
std::vector<OutputType>& out,
bool ignoreEmpty) {
detail::internalSplit<OutputType>(
detail::prepareDelim(delimiter),
StringPiece(input),
std::back_inserter(out),
ignoreEmpty);
}
template<class Delim, class String, class OutputType>
void split(const Delim& delimiter,
const String& input,
fbvector<OutputType>& out,
bool ignoreEmpty) {
detail::internalSplit<OutputType>(
detail::prepareDelim(delimiter),
StringPiece(input),
std::back_inserter(out),
ignoreEmpty);
}
template<class OutputValueType, class Delim, class String,
class OutputIterator>
void splitTo(const Delim& delimiter,
const String& input,
OutputIterator out,
bool ignoreEmpty) {
detail::internalSplit<OutputValueType>(
detail::prepareDelim(delimiter),
StringPiece(input),
out,
ignoreEmpty);
}
template<bool exact,
class Delim,
class... StringPieces>
bool split(const Delim& delimiter,
StringPiece input,
StringPiece& outHead,
StringPieces&... outTail) {
return detail::splitFixed<exact>(
detail::prepareDelim(delimiter),
input,
outHead,
outTail...);
}
namespace detail {
template <class Iterator>
struct IsStringContainerIterator :
IsSomeString<typename std::iterator_traits<Iterator>::value_type> {
};
template <class Delim, class Iterator, class String>
void internalJoinAppend(Delim delimiter,
Iterator begin,
Iterator end,
String& output) {
assert(begin != end);
if (std::is_same<Delim, StringPiece>::value &&
delimSize(delimiter) == 1) {
internalJoinAppend(delimFront(delimiter), begin, end, output);
return;
}
toAppend(*begin, &output);
while (++begin != end) {
toAppend(delimiter, *begin, &output);
}
}
template <class Delim, class Iterator, class String>
typename std::enable_if<IsStringContainerIterator<Iterator>::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 <class Delim, class Iterator, class String>
typename std::enable_if<!IsStringContainerIterator<Iterator>::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 <class Delim, class Iterator, class String>
void join(const Delim& delimiter,
Iterator begin,
Iterator end,
String& output) {
detail::internalJoin(
detail::prepareDelim(delimiter),
begin,
end,
output);
}
template <class String1, class String2>
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 <class String1, class String2>
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<class InputString, class OutputString>
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<class InputString, class OutputString>
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 <class OutIt>
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_ */
-335
Ver Arquivo
@@ -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 <cerrno>
#include <cstdarg>
#include <cstring>
#include <stdexcept>
#include <iterator>
#include <glog/logging.h>
#undef FOLLY_DEMANGLE
#if defined(__GNUG__) && __GNUG__ >= 4
# include <cxxabi.h>
# 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<std::string>("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<std::string>("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<StringPiece>(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<fbstring>(
"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<const uint8_t*>(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<char>(p[i]) : '.');
line.push_back(c);
}
line.append(16 - n, ' ');
line.push_back('|');
DCHECK_EQ(line.size(), 78);
return n;
}
} // namespace detail
} // namespace folly
-502
Ver Arquivo
@@ -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 <exception>
#include <string>
#include <boost/type_traits.hpp>
#ifdef __GNUC__
# include <ext/hash_set>
# include <ext/hash_map>
#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:
* <ASCII 254> -> \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 <class String>
void cEscape(StringPiece str, String& out);
/**
* Similar to cEscape above, but returns the escaped string.
*/
template <class String>
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 <class String>
void cUnescape(StringPiece str, String& out, bool strict = true);
/**
* Similar to cUnescape above, but returns the escaped string.
*/
template <class String>
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 <class String>
void uriEscape(StringPiece str,
String& out,
UriEscapeMode mode = UriEscapeMode::ALL);
/**
* Similar to uriEscape above, but returns the escaped string.
*/
template <class String>
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 <class String>
void uriUnescape(StringPiece str,
String& out,
UriEscapeMode mode = UriEscapeMode::ALL);
/**
* Similar to uriUnescape above, but returns the unescaped string.
*/
template <class String>
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 <class String1, class String2>
void backslashify(const String1& input, String2& output, bool hex_style=false);
template <class String>
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 <class String1, class String2>
void humanify(const String1& input, String2& output);
template <class String>
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<class InputString, class OutputString>
bool hexlify(const InputString& input, OutputString& output,
bool append=false);
/**
* Same functionality as Python's binascii.unhexlify. Returns true
* on successful conversion.
*/
template<class InputString, class OutputString>
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 <mrabkin@fb.com>
*/
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 <class OutIt>
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<fbstring>(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 "<unknown exception>";
}
}
/*
* 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<folly::StringPiece> v;
* folly::split(":", "asd:bsd", v);
*
* std::set<StringPiece> s;
* folly::splitTo<StringPiece>(":", "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<class Delim, class String, class OutputType>
void split(const Delim& delimiter,
const String& input,
std::vector<OutputType>& out,
bool ignoreEmpty = false);
template<class Delim, class String, class OutputType>
void split(const Delim& delimiter,
const String& input,
folly::fbvector<OutputType>& out,
bool ignoreEmpty = false);
template<class OutputValueType, class Delim, class String,
class OutputIterator>
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<false>(':', "a:b:c", x, y))
* assert(x == "a" && y == "b:c");
*/
template<bool exact = true,
class Delim,
class... StringPieces>
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 <class Delim, class Iterator, class String>
void join(const Delim& delimiter,
Iterator begin,
Iterator end,
String& output);
template <class Delim, class Container, class String>
void join(const Delim& delimiter,
const Container& container,
String& output) {
join(delimiter, container.begin(), container.end(), output);
}
template <class Delim, class Value, class String>
void join(const Delim& delimiter,
const std::initializer_list<Value>& values,
String& output) {
join(delimiter, values.begin(), values.end(), output);
}
template <class Delim, class Container>
std::string join(const Delim& delimiter,
const Container& container) {
std::string output;
join(delimiter, container.begin(), container.end(), output);
return output;
}
template <class Delim, class Value>
std::string join(const Delim& delimiter,
const std::initializer_list<Value>& 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 <class C>
struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
size_t operator()(const folly::basic_fbstring<C> & s) const {
return hash<const C*>::operator()(s.c_str());
}
};
template <class C>
struct hash<std::basic_string<C> > : private hash<const C*> {
size_t operator()(const std::basic_string<C> & s) const {
return hash<const C*>::operator()(s.c_str());
}
};
} // namespace __gnu_cxx
#endif
// Hook into boost's type traits
namespace boost {
template <class T>
struct has_nothrow_constructor<folly::basic_fbstring<T> > : true_type {
enum { value = true };
};
} // namespace boost
#include "folly/String-inl.h"
#endif
-779
Ver Arquivo
@@ -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 <sys/prctl.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <wait.h>
#include <array>
#include <algorithm>
#include <system_error>
#include <boost/container/flat_set.hpp>
#include <boost/range/adaptors.hpp>
#include <glog/logging.h>
#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<std::string>(
"Invalid ProcessReturnCode: ", rawStatus_));
}
void ProcessReturnCode::enforce(State expected) const {
State s = state();
if (s != expected) {
throw std::logic_error(to<std::string>("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<std::string>("exited with status ", exitStatus());
case KILLED:
return to<std::string>("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<std::string>(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<const char*[]> cloneStrings(const std::vector<std::string>& s) {
std::unique_ptr<const char*[]> 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<std::string>("Only fds 0, 1, 2 are valid for action=PIPE: ", fd));
}
}
fdActions_[fd] = action;
return *this;
}
Subprocess::Subprocess(
const std::vector<std::string>& argv,
const Options& options,
const char* executable,
const std::vector<std::string>* 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<std::string>* 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<const char*[]> 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<const char*[]> argv,
const char* executable,
const Options& optionsIn,
const std::vector<std::string>* 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<const char*[]> argv,
const char* executable,
Options& options,
const std::vector<std::string>* env,
int errFd) {
// Parent work, pre-fork: create pipes
std::vector<int> 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<char**>(argv.get());
// Set up environment
std::unique_ptr<const char*[]> envHolder;
char** envVec;
if (env) {
envHolder = cloneStrings(*env);
envVec = const_cast<char**>(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<const uint8_t*, size_t> 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<char[]> 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<std::string, std::string> 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<std::string, std::string> out;
if (outBufs.first) {
outBufs.first->coalesce();
out.first.assign(reinterpret_cast<const char*>(outBufs.first->data()),
outBufs.first->length());
}
if (outBufs.second) {
outBufs.second->coalesce();
out.second.assign(reinterpret_cast<const char*>(outBufs.second->data()),
outBufs.second->length());
}
return out;
}
std::pair<IOBufQueue, IOBufQueue> 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<IOBufQueue, IOBufQueue> 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<pollfd> fds;
fds.reserve(pipes_.size());
std::vector<int> 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<std::string>(
"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
-528
Ver Arquivo
@@ -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<string>
* 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 <sys/types.h>
#include <signal.h>
#include <wait.h>
#include <exception>
#include <vector>
#include <string>
#include <boost/container/flat_map.hpp>
#include <boost/operators.hpp>
#include <boost/noncopyable.hpp>
#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 <status>"
* "killed by signal <signal>"
* "killed by signal <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<Options> {
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<int, int> 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<std::string>& argv,
const Options& options = Options(),
const char* executable = nullptr,
const std::vector<std::string>* 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<std::string>* 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<IOBufQueue, IOBufQueue> communicateIOBuf(
IOBufQueue input = IOBufQueue());
std::pair<std::string, std::string> 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<bool(int, int)> 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<const char*[]> argv,
const char* executable,
const Options& options,
const std::vector<std::string>* env);
void spawnInternal(
std::unique_ptr<const char*[]> argv,
const char* executable,
Options& options,
const std::vector<std::string>* 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<PipeInfo> {
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<PipeInfo> 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_ */
-692
Ver Arquivo
@@ -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 <type_traits>
#include <mutex>
#include <boost/thread.hpp>
#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 <class T>
struct HasLockUnlock {
enum { value = IsOneOf<T,
std::mutex, std::recursive_mutex,
boost::mutex, boost::recursive_mutex, boost::shared_mutex
#ifndef __APPLE__ // OSX doesn't have timed mutexes
,std::timed_mutex, std::recursive_timed_mutex,
boost::timed_mutex, boost::recursive_timed_mutex
#endif
>::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 <class T>
typename std::enable_if<
HasLockUnlock<T>::value && !std::is_same<T, boost::shared_mutex>::value>::type
acquireRead(T& mutex) {
mutex.lock();
}
/**
* Special case for boost::shared_mutex.
*/
template <class T>
typename std::enable_if<std::is_same<T, boost::shared_mutex>::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 <class T>
typename std::enable_if<std::is_same<T, boost::shared_mutex>::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 <class T>
typename std::enable_if<HasLockUnlock<T>::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 <class T>
typename std::enable_if<
IsOneOf<T, std::timed_mutex, std::recursive_timed_mutex>::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 <class T>
typename std::enable_if<
IsOneOf<T, boost::shared_mutex, boost::timed_mutex,
boost::recursive_timed_mutex>::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 <class T>
typename std::enable_if<
HasLockUnlock<T>::value && !std::is_same<T, boost::shared_mutex>::value>::type
releaseRead(T& mutex) {
mutex.unlock();
}
/**
* Special case for boost::shared_mutex.
*/
template <class T>
typename std::enable_if<std::is_same<T, boost::shared_mutex>::value>::type
releaseRead(T& mutex) {
mutex.unlock_shared();
}
/**
* Releases a mutex previously acquired for reading-writing by calling
* .unlock().
*/
template <class T>
typename std::enable_if<HasLockUnlock<T>::value>::type
releaseReadWrite(T& mutex) {
mutex.unlock();
}
} // namespace detail
/**
* Synchronized<T> 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<T> 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 <class T, class Mutex = boost::shared_mutex>
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<T> 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 <class P1, class P2>
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 <class P1, class P2>
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 <class T, class M>
void swap(Synchronized<T, M>& lhs, Synchronized<T, M>& rhs) {
lhs.swap(rhs);
}
/**
* SYNCHRONIZED is the main facility that makes Synchronized<T>
* 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<vector<int>> svector;
* ...
* SYNCHRONIZED (svector) { ... use svector as a vector<int> ... }
* or
* SYNCHRONIZED (v, svector) { ... use v as a vector<int> ... }
*
* 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 <class P1, class P2>
void lockInOrder(P1& p1, P2& p2) {
if (static_cast<const void*>(p1.operator->()) >
static_cast<const void*>(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_
-43
Ver Arquivo
@@ -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<SysArena> 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<std::mutex> lock(zombiesMutex_);
zombies_.merge(std::move(arena));
}
} // namespace folly
-81
Ver Arquivo
@@ -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 <utility>
#include <mutex>
#include <limits>
#include <boost/intrusive/slist.hpp>
#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<SysArena> arena_; // per-thread arena
};
} // namespace folly
#endif /* FOLLY_THREADCACHEDARENA_H_ */
-176
Ver Arquivo
@@ -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 <atomic>
#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 IntT, class Tag=IntT>
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<IntT> target_;
std::atomic<uint32_t> cacheSize_;
ThreadLocalPtr<IntCache,Tag> cache_; // Must be last for dtor ordering
// This should only ever be modified by one thread
struct IntCache {
ThreadCachedInt* parent_;
mutable std::atomic<IntT> val_;
mutable uint32_t numUpdates_;
std::atomic<bool> 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
-339
Ver Arquivo
@@ -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 <boost/iterator/iterator_facade.hpp>
#include "folly/Likely.h"
#include <type_traits>
namespace folly {
enum class TLPDestructionMode {
THIS_THREAD,
ALL_THREADS
};
} // namespace
#include "folly/detail/ThreadLocalDetail.h"
namespace folly {
template<class T, class Tag> class ThreadLocalPtr;
template<class T, class Tag=void>
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<T,Tag>::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<T,Tag> 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 T, class Tag=void>
class ThreadLocalPtr {
public:
ThreadLocalPtr() : id_(threadlocal_detail::StaticMeta<Tag>::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<T*>(threadlocal_detail::StaticMeta<Tag>::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<Tag>::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 <class Deleter>
void reset(T* newPtr, Deleter deleter) {
threadlocal_detail::ElementWrapper& w =
threadlocal_detail::StaticMeta<Tag>::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<T,Tag>;
threadlocal_detail::StaticMeta<Tag>& 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<T*>(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<Tag>::instance()),
lock_(nullptr),
id_(0) {
}
private:
explicit Accessor(int id)
: meta_(threadlocal_detail::StaticMeta<Tag>::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 <Tag>
Accessor accessAllThreads() const {
static_assert(!std::is_same<Tag, void>::value,
"Must use a unique Tag to use the accessAllThreads feature");
return Accessor(id_);
}
private:
void destroy() {
if (id_) {
threadlocal_detail::StaticMeta<Tag>::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_ */
-76
Ver Arquivo
@@ -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 <algorithm>
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<int64_t>::max() :
timeouts_.get<BY_EXPIRATION>().begin()->expiration);
}
bool TimeoutQueue::erase(Id id) {
return timeouts_.get<BY_ID>().erase(id);
}
int64_t TimeoutQueue::runInternal(int64_t now, bool onceOnly) {
auto& byExpiration = timeouts_.get<BY_EXPIRATION>();
int64_t nextExp;
do {
auto end = byExpiration.upper_bound(now);
std::vector<Event> 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
-132
Ver Arquivo
@@ -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 <stdint.h>
#include <functional>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
namespace folly {
class TimeoutQueue {
public:
typedef int64_t Id;
typedef std::function<void(Id, int64_t)> 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::member<
Event, Id, &Event::id
>>,
boost::multi_index::ordered_non_unique<boost::multi_index::member<
Event, int64_t, &Event::expiration
>>
>
> Set;
enum {
BY_ID=0,
BY_EXPIRATION=1
};
Set timeouts_;
Id nextId_;
};
} // namespace folly
#endif /* FOLLY_TIMEOUTQUEUE_H_ */
-502
Ver Arquivo
@@ -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 <memory>
#include <limits>
#include <type_traits>
#include <bits/c++config.h>
#include <boost/type_traits.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/has_xxx.hpp>
#include <boost/mpl/not.hpp>
namespace folly {
/**
* IsRelocatable<T>::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<SomeStruct> 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 <class T> struct name ## _is_true \
: std::is_same<typename T::name, std::true_type> {}; \
template <class T> struct has_true_ ## name \
: std::conditional< \
has_ ## name <T>::value, \
name ## _is_true<T>, \
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 <class T> struct IsTriviallyCopyable
: std::integral_constant<bool,
!std::is_class<T>::value ||
// TODO: add alternate clause is_trivially_copyable, when available
traits_detail::has_true_IsTriviallyCopyable<T>::value
> {};
template <class T> struct IsRelocatable
: std::integral_constant<bool,
!std::is_class<T>::value ||
// TODO add this line (and some tests for it) when we upgrade to gcc 4.7
//std::is_trivially_move_constructible<T>::value ||
IsTriviallyCopyable<T>::value ||
traits_detail::has_true_IsRelocatable<T>::value
> {};
template <class T> struct IsZeroInitializable
: std::integral_constant<bool,
!std::is_class<T>::value ||
traits_detail::has_true_IsZeroInitializable<T>::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<class T1, class T2>
* FOLLY_ASSUME_RELOCATABLE(MyType<T1, T2>)
*/
#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<class T1, class T2>
* FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(MyType<T1, T2>)
*/
#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 T1, class T2> 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 <class T1> FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1>) } \
namespace boost { \
template <class T1> FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__<T1>) }
// Use this macro ONLY at global level (no namespace)
#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(...) \
namespace folly { \
template <class T1, class T2> \
FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2>) } \
namespace boost { \
template <class T1, class T2> \
FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__<T1, T2>) }
// Use this macro ONLY at global level (no namespace)
#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(...) \
namespace folly { \
template <class T1, class T2, class T3> \
FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2, T3>) } \
namespace boost { \
template <class T1, class T2, class T3> \
FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__<T1, T2, T3>) }
// Use this macro ONLY at global level (no namespace)
#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_4(...) \
namespace folly { \
template <class T1, class T2, class T3, class T4> \
FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2, T3, T4>) } \
namespace boost { \
template <class T1, class T2, class T3, class T4> \
FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__<T1, T2, T3, T4>) }
/**
* 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 <class T, class U>
struct pair;
#ifndef _GLIBCXX_USE_FB
template <class T, class R, class A>
class basic_string;
#else
template <class T, class R, class A, class S>
class basic_string;
#endif
template <class T, class A>
class vector;
template <class T, class A>
class deque;
template <class T, class A>
class list;
template <class T, class C, class A>
class set;
template <class K, class V, class C, class A>
class map;
template <class T>
class shared_ptr;
}
namespace boost {
template <class T> class shared_ptr;
template <class T, class U>
struct has_nothrow_constructor< std::pair<T, U> >
: ::boost::mpl::and_< has_nothrow_constructor<T>,
has_nothrow_constructor<U> > {};
} // namespace boost
namespace folly {
// STL commonly-used types
template <class T, class U>
struct IsRelocatable< std::pair<T, U> >
: ::boost::mpl::and_< IsRelocatable<T>, IsRelocatable<U> > {};
// Is T one of T1, T2, ..., Tn?
template <class T, class... Ts>
struct IsOneOf {
enum { value = false };
};
template <class T, class T1, class... Ts>
struct IsOneOf<T, T1, Ts...> {
enum { value = std::is_same<T, T1>::value || IsOneOf<T, Ts...>::value };
};
/**
* A traits class to check for incomplete types.
*
* Example:
*
* struct FullyDeclared {}; // complete type
* struct ForwardDeclared; // incomplete type
*
* is_complete<int>::value // evaluates to true
* is_complete<FullyDeclared>::value // evaluates to true
* is_complete<ForwardDeclared>::value // evaluates to false
*
* struct ForwardDeclared {}; // declared, at last
*
* is_complete<ForwardDeclared>::value // now it evaluates to true
*
* @author: Marcelo Juchem <marcelo@fb.com>
*/
template <typename T>
class is_complete {
template <unsigned long long> struct sfinae {};
template <typename U>
constexpr static bool test(sfinae<sizeof(U)>*) { return true; }
template <typename> constexpr static bool test(...) { return false; }
public:
constexpr static bool value = test<T>(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 <marcelo@fb.com>
*/
namespace detail {
template <typename T, bool>
struct is_negative_impl {
constexpr static bool check(T x) { return x < 0; }
};
template <typename T>
struct is_negative_impl<T, false> {
constexpr static bool check(T x) { return false; }
};
template <typename RHS, RHS rhs, typename LHS>
bool less_than_impl(
typename std::enable_if<
(rhs <= std::numeric_limits<LHS>::max()
&& rhs > std::numeric_limits<LHS>::min()),
LHS
>::type const lhs
) {
return lhs < rhs;
}
template <typename RHS, RHS rhs, typename LHS>
bool less_than_impl(
typename std::enable_if<
(rhs > std::numeric_limits<LHS>::max()),
LHS
>::type const
) {
return true;
}
template <typename RHS, RHS rhs, typename LHS>
bool less_than_impl(
typename std::enable_if<
(rhs <= std::numeric_limits<LHS>::min()),
LHS
>::type const
) {
return false;
}
template <typename RHS, RHS rhs, typename LHS>
bool greater_than_impl(
typename std::enable_if<
(rhs <= std::numeric_limits<LHS>::max()
&& rhs >= std::numeric_limits<LHS>::min()),
LHS
>::type const lhs
) {
return lhs > rhs;
}
template <typename RHS, RHS rhs, typename LHS>
bool greater_than_impl(
typename std::enable_if<
(rhs > std::numeric_limits<LHS>::max()),
LHS
>::type const
) {
return false;
}
template <typename RHS, RHS rhs, typename LHS>
bool greater_than_impl(
typename std::enable_if<
(rhs < std::numeric_limits<LHS>::min()),
LHS
>::type const
) {
return true;
}
} // namespace detail {
// same as `x < 0`
template <typename T>
constexpr bool is_negative(T x) {
return folly::detail::is_negative_impl<T, std::is_signed<T>::value>::check(x);
}
// same as `x <= 0`
template <typename T>
constexpr bool is_non_positive(T x) { return !x || folly::is_negative(x); }
template <typename RHS, RHS rhs, typename LHS>
bool less_than(LHS const lhs) {
return detail::less_than_impl<
RHS, rhs, typename std::remove_reference<LHS>::type
>(lhs);
}
template <typename RHS, RHS rhs, typename LHS>
bool greater_than(LHS const lhs) {
return detail::greater_than_impl<
RHS, rhs, typename std::remove_reference<LHS>::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 <typename TTheClass_, typename RTheReturn_, typename... TTheArgs_> \
class classname<TTheClass_, RTheReturn_(TTheArgs_...) cv_qual> { \
template < \
typename UTheClass_, RTheReturn_ (UTheClass_::*)(TTheArgs_...) cv_qual \
> struct sfinae {}; \
template <typename UTheClass_> \
constexpr static bool test(sfinae<UTheClass_, &UTheClass_::func_name>*) \
{ return true; } \
template <typename> \
constexpr static bool test(...) { return false; } \
public: \
constexpr static bool value = test<TTheClass_>(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<Foo, int() const>::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<Foo, int()>::value;
* cout << "Does class Foo have a member int test() const? "
* << boolalpha << has_test_traits<Foo, int() const>::value;
* cout << "Does class Bar have a member double test(const string&, long)? "
* << boolalpha << has_test_traits<Bar, double(const string&, long)>::value;
* }
*
* @author: Marcelo Juchem <marcelo@fb.com>
*/
#define FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(classname, func_name) \
template <typename, typename> 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_
-54
Ver Arquivo
@@ -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<char>(cp);
} else if (cp <= 0x7FF) {
result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (cp >> 6));
} else if (cp <= 0xFFFF) {
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = (0x80 | static_cast<char>((0x3f & (cp >> 6))));
result[0] = (0xE0 | static_cast<char>(cp >> 12));
} else if (cp <= 0x10FFFF) {
result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast<char>(0xF0 | (cp >> 18));
}
return result;
}
//////////////////////////////////////////////////////////////////////
}
-39
Ver Arquivo
@@ -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
-49
Ver Arquivo
@@ -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 <class String>
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
-95
Ver Arquivo
@@ -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 <ctype.h>
#include <boost/regex.hpp>
namespace folly {
namespace {
fbstring submatch(const boost::cmatch& m, size_t idx) {
auto& sub = m[idx];
return fbstring(sub.first, sub.second);
}
template <class String>
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<uint32_t>(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
-77
Ver Arquivo
@@ -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 <class String>
String toString() const;
std::string str() const { return toString<std::string>(); }
fbstring fbstr() const { return toString<fbstring>(); }
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_ */
-129
Ver Arquivo
@@ -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<uint64_t>((val << 1) ^ (val >> 63));
}
inline int64_t decodeZigZag(uint64_t val) {
return static_cast<int64_t>((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<const int8_t*>(data.begin());
const int8_t* end = reinterpret_cast<const int8_t*>(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<uint64_t>(*p++ & 0x7f) << shift;
shift += 7;
}
if (p == end) throw std::invalid_argument("Invalid varint value");
val |= static_cast<uint64_t>(*p++) << shift;
}
data.advance(p - begin);
return val;
}
} // namespaces
#endif /* FOLLY_VARINT_H_ */
-37
Ver Arquivo
@@ -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
-93
Ver Arquivo
@@ -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 <iterator>
#include <type_traits>
#include <boost/iterator/iterator_adaptor.hpp>
namespace folly {
template <class BaseIter> 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 Ref, class Value>
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 <class BaseIter>
struct BitIteratorBase {
static_assert(std::is_integral<typename BaseIter::value_type>::value,
"BitIterator may only be used with integral types");
typedef boost::iterator_adaptor<
BitIterator<BaseIter>, // Derived
BaseIter, // Base
bool, // Value
boost::use_default, // CategoryOrTraversal
bititerator_detail::BitReference<
typename std::iterator_traits<BaseIter>::reference,
typename std::iterator_traits<BaseIter>::value_type
>, // Reference
ssize_t> type;
};
} // namespace bititerator_detail
} // namespace folly
#endif /* FOLLY_DETAIL_BITITERATORDETAIL_H_ */
-47
Ver Arquivo
@@ -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_ */
@@ -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 <type_traits>
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<int, void, char, int>::value == 3
* GetIndex<int, void, char>::value -> fails to compile
*/
template <typename... Types> 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<T, T, Types...> partial specialization.
template <typename T, typename... Types>
struct GetTypeIndex<T, T, Types...> {
static const size_t value = 1;
};
template <typename T, typename U, typename... Types>
struct GetTypeIndex<T, U, Types...> {
static const size_t value = 1 + GetTypeIndex<T, Types...>::value;
};
// Generalize std::is_same for variable number of type arguments
template <typename... Types>
struct IsSameType;
template <>
struct IsSameType<> {
static const bool value = true;
};
template <typename T>
struct IsSameType<T> {
static const bool value = true;
};
template <typename T, typename U, typename... Types>
struct IsSameType<T, U, Types...> {
static const bool value =
std::is_same<T,U>::value && IsSameType<U, Types...>::value;
};
// Define type as the type of all T in (non-empty) Types..., asserting that
// all types in Types... are the same.
template <typename... Types>
struct SameType;
template <typename T, typename... Types>
struct SameType<T, Types...> {
typedef T type;
static_assert(IsSameType<T, Types...>::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 <typename V, typename T>
struct VisitorResult1 {
typedef typename std::result_of<V (T*)>::type type;
};
// Determine the result type of applying a visitor of type V on a const pointer
// to type T.
template <typename V, typename T>
struct ConstVisitorResult1 {
typedef typename std::result_of<V (const T*)>::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 <typename V, typename... Types>
struct VisitorResult {
typedef typename SameType<
typename VisitorResult1<V,Types>::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 <typename V, typename... Types>
struct ConstVisitorResult {
typedef typename SameType<
typename ConstVisitorResult1<V,Types>::type...>::type type;
};
template <typename V, typename R, typename... Types> struct ApplyVisitor1;
template <typename V, typename R>
struct ApplyVisitor1<V, R> {
R operator()(size_t index, V&& visitor, void* ptr) const {
CHECK(false); // NOTREACHED
}
};
template <typename V, typename R, typename T, typename... Types>
struct ApplyVisitor1<V, R, T, Types...> {
R operator()(size_t index, V&& visitor, void* ptr) const {
return (index == 1 ? visitor(static_cast<T*>(ptr)) :
ApplyVisitor1<V, R, Types...>()(
index - 1, std::forward<V>(visitor), ptr));
}
};
template <typename V, typename R, typename... Types> struct ApplyConstVisitor1;
template <typename V, typename R>
struct ApplyConstVisitor1<V, R> {
R operator()(size_t index, V&& visitor, void* ptr) const {
CHECK(false); // NOTREACHED
}
};
template <typename V, typename R, typename T, typename... Types>
struct ApplyConstVisitor1<V, R, T, Types...> {
R operator()(size_t index, V&& visitor, void* ptr) const {
return (index == 1 ? visitor(static_cast<const T*>(ptr)) :
ApplyConstVisitor1<V, R, Types...>()(
index - 1, std::forward<V>(visitor), ptr));
}
};
template <typename V, typename... Types>
struct ApplyVisitor
: ApplyVisitor1<
V, typename VisitorResult<V, Types...>::type, Types...> {
};
template <typename V, typename... Types>
struct ApplyConstVisitor
: ApplyConstVisitor1<
V, typename ConstVisitorResult<V, Types...>::type, Types...> {
};
} // namespace dptr_detail
} // namespace folly
#endif /* FOLLY_DETAIL_DISCRIMINATEDPTRDETAIL_H_ */
-112
Ver Arquivo
@@ -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 <cerrno>
#include <unistd.h>
#include <sys/uio.h>
/**
* 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<class F, class... Args>
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 <class F, class... Offset>
ssize_t wrapFull(F f, int fd, void* buf, size_t count, Offset... offset) {
char* b = static_cast<char*>(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 <class F, class... Offset>
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<char*>(iov->iov_base) + r;
iov->iov_len -= r;
r = 0;
}
}
} while (count);
return totalBytes;
}
}} // namespaces
#endif /* FOLLY_DETAIL_FILEUTILDETAIL_H_ */
@@ -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 <cstdint>
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 <int DEG>
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<DEG>& 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<DEG> 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<DEG>& 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<DEG>& 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_ */
-84
Ver Arquivo
@@ -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 <atomic>
#include <limits>
#include <assert.h>
#include <errno.h>
#include <linux/futex.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <boost/noncopyable.hpp>
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 <template <typename> class Atom = std::atomic>
struct Futex : Atom<uint32_t>, boost::noncopyable {
explicit Futex(uint32_t init = 0) : Atom<uint32_t>(init) {}
/** Puts the thread to sleep if this->load() == expected. Returns true when
* it is returning because it has consumed a wake() event, false for any
* other return (signal, this->load() != expected, or spurious wakeup). */
bool futexWait(uint32_t expected, uint32_t waitMask = -1);
/** Wakens up to count waiters where (waitMask & wakeMask) != 0,
* returning the number of awoken threads. */
int futexWake(int count = std::numeric_limits<int>::max(),
uint32_t wakeMask = -1);
};
template <>
inline bool Futex<std::atomic>::futexWait(uint32_t expected,
uint32_t waitMask) {
assert(sizeof(*this) == sizeof(int));
int rv = syscall(SYS_futex,
this, /* addr1 */
FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG, /* op */
expected, /* val */
nullptr, /* timeout */
nullptr, /* addr2 */
waitMask); /* val3 */
assert(rv == 0 || (errno == EWOULDBLOCK || errno == EINTR));
return rv == 0;
}
template <>
inline int Futex<std::atomic>::futexWake(int count, uint32_t wakeMask) {
assert(sizeof(*this) == sizeof(int));
int rv = syscall(SYS_futex,
this, /* addr1 */
FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, /* op */
count, /* val */
nullptr, /* timeout */
nullptr, /* addr2 */
wakeMask); /* val3 */
assert(rv >= 0);
return rv;
}
}}
-104
Ver Arquivo
@@ -1,104 +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_GROUPVARINTDETAIL_H_
#define FOLLY_DETAIL_GROUPVARINTDETAIL_H_
#include <stddef.h>
namespace folly {
template <typename T>
class GroupVarint;
namespace detail {
template <typename T>
struct GroupVarintTraits;
template <>
struct GroupVarintTraits<uint32_t> {
enum {
kGroupSize = 4,
kHeaderSize = 1,
};
};
template <>
struct GroupVarintTraits<uint64_t> {
enum {
kGroupSize = 5,
kHeaderSize = 2,
};
};
template <typename T>
class GroupVarintBase {
protected:
typedef GroupVarintTraits<T> Traits;
enum { kHeaderSize = Traits::kHeaderSize };
public:
typedef T type;
/**
* Number of integers encoded / decoded in one pass.
*/
enum { kGroupSize = Traits::kGroupSize };
/**
* Maximum encoded size.
*/
enum { kMaxSize = kHeaderSize + sizeof(type) * kGroupSize };
/**
* Maximum size for n values.
*/
static size_t maxSize(size_t n) {
// Full groups
size_t total = (n / kGroupSize) * kFullGroupSize;
// Incomplete last group, if any
n %= kGroupSize;
if (n) {
total += kHeaderSize + n * sizeof(type);
}
return total;
}
/**
* Size of n values starting at p.
*/
static size_t totalSize(const T* p, size_t n) {
size_t size = 0;
for (; n >= kGroupSize; n -= kGroupSize, p += kGroupSize) {
size += Derived::size(p);
}
if (n) {
size += Derived::partialSize(p, n);
}
return size;
}
private:
typedef GroupVarint<T> Derived;
enum { kFullGroupSize = kHeaderSize + kGroupSize * sizeof(type) };
};
} // namespace detail
} // namespace folly
#endif /* FOLLY_DETAIL_GROUPVARINTDETAIL_H_ */

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais