/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// @author Mark Rabkin (mrabkin@fb.com)
// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com)
-#ifndef FOLLY_RANGE_H_
-#define FOLLY_RANGE_H_
+#pragma once
-#include <folly/Portability.h>
#include <folly/FBString.h>
+#include <folly/Portability.h>
+#include <folly/SpookyHashV2.h>
+#include <folly/portability/Constexpr.h>
+#include <folly/portability/String.h>
+
#include <algorithm>
#include <boost/operators.hpp>
+#include <climits>
+#include <cstddef>
#include <cstring>
#include <glog/logging.h>
#include <iosfwd>
#include <folly/CpuId.h>
#include <folly/Traits.h>
#include <folly/Likely.h>
+#include <folly/detail/RangeCommon.h>
+#include <folly/detail/RangeSse42.h>
// Ignore shadowing warnings within this file, so includers can use -Wshadow.
#pragma GCC diagnostic push
return *--i;
}
+/*
+ * Use IsCharPointer<T>::type to enable const char* or char*.
+ * Use IsCharPointer<T>::const_type to enable only const char*.
+ */
+template <class T> struct IsCharPointer {};
+
+template <>
+struct IsCharPointer<char*> {
+ typedef int type;
+};
+
+template <>
+struct IsCharPointer<const char*> {
+ typedef int const_type;
+ typedef int type;
+};
+
} // namespace detail
/**
static const size_type npos;
// Works for all iterators
- Range() : b_(), e_() {
+ constexpr Range() : b_(), e_() {
}
+ constexpr Range(const Range&) = default;
+ constexpr Range(Range&&) = default;
+
public:
// Works for all iterators
- Range(Iter start, Iter end) : b_(start), e_(end) {
+ constexpr Range(Iter start, Iter end) : b_(start), e_(end) {
}
// Works only for random-access iterators
- Range(Iter start, size_t size)
+ constexpr Range(Iter start, size_t size)
: b_(start), e_(start + size) { }
-#if FOLLY_HAVE_CONSTEXPR_STRLEN
- // Works only for Range<const char*>
- /* implicit */ constexpr Range(Iter str)
- : b_(str), e_(str + strlen(str)) {}
-#else
- // Works only for Range<const char*>
- /* implicit */ Range(Iter str)
- : b_(str), e_(str + strlen(str)) {}
-#endif
- // Works only for Range<const char*>
+# if !__clang__ || __CLANG_PREREQ(3, 7) // Clang 3.6 crashes on this line
+ /* implicit */ Range(std::nullptr_t) = delete;
+# endif
+
+ template <class T = Iter, typename detail::IsCharPointer<T>::type = 0>
+ constexpr /* implicit */ Range(Iter str)
+ : b_(str), e_(str + constexpr_strlen(str)) {}
+
+ template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
/* implicit */ Range(const std::string& str)
: b_(str.data()), e_(b_ + str.size()) {}
- // Works only for Range<const char*>
+ template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
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*>
+
+ template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
Range(const std::string& str,
std::string::size_type startFrom,
std::string::size_type size) {
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*>
+
+ Range(const Range& other,
+ size_type first,
+ size_type length = npos)
+ : Range(other.subpiece(first, length))
+ { }
+
+ template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
/* implicit */ Range(const fbstring& str)
: b_(str.data()), e_(b_ + str.size()) { }
- // Works only for Range<const char*>
+
+ template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
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*>
+
+ template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
Range(const fbstring& str, fbstring::size_type startFrom,
fbstring::size_type size) {
if (UNLIKELY(startFrom > str.size())) {
template <class OtherIter, typename std::enable_if<
(!std::is_same<Iter, OtherIter>::value &&
std::is_convertible<OtherIter, Iter>::value), int>::type = 0>
- /* implicit */ Range(const Range<OtherIter>& other)
+ constexpr /* implicit */ Range(const Range<OtherIter>& other)
: b_(other.begin()),
e_(other.end()) {
}
(!std::is_same<Iter, OtherIter>::value &&
!std::is_convertible<OtherIter, Iter>::value &&
std::is_constructible<Iter, const OtherIter&>::value), int>::type = 0>
- explicit Range(const Range<OtherIter>& other)
+ constexpr explicit Range(const Range<OtherIter>& other)
: b_(other.begin()),
e_(other.end()) {
}
+ Range& operator=(const Range& rhs) & = default;
+ Range& operator=(Range&& rhs) & = default;
+
void clear() {
b_ = Iter();
e_ = Iter();
reset(str.data(), str.size());
}
- size_type size() const {
- assert(b_ <= e_);
+ constexpr size_type size() const {
+ // It would be nice to assert(b_ <= e_) here. This can be achieved even
+ // in a C++11 compatible constexpr function:
+ // http://ericniebler.com/2014/09/27/assert-and-constexpr-in-cxx11/
+ // Unfortunately current gcc versions have a bug causing it to reject
+ // this check in a constexpr function:
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71448
return e_ - b_;
}
size_type walk_size() const {
- assert(b_ <= e_);
return std::distance(b_, e_);
}
bool empty() const { return b_ == e_; }
assert(b_ < e_);
return detail::value_before(e_);
}
- // Works only for Range<const char*>
+ // Works only for Range<const char*> and Range<char*>
std::string str() const { return std::string(b_, size()); }
std::string toString() const { return str(); }
- // Works only for Range<const char*>
+ // Works only for Range<const char*> and Range<char*>
fbstring fbstr() const { return fbstring(b_, size()); }
fbstring toFbstring() const { return fbstr(); }
return const_range_type(*this);
};
- // Works only for Range<const char*> (and Range<char*>)
+ // Works only for Range<const char*> and Range<char*>
int compare(const const_range_type& 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;
+ if (r == 0 && tsize != osize) {
+ // We check the signed bit of the subtraction and bit shift it
+ // to produce either 0 or 2. The subtraction yields the
+ // comparison values of either -1 or 1.
+ r = (static_cast<int>(
+ (osize - tsize) >> (CHAR_BIT * sizeof(size_t) - 1)) << 1) - 1;
+ }
return r;
}
return b_[i];
}
- // Works only for Range<const char*>
+ // Do NOT use this function, which was left behind for backwards
+ // compatibility. Use SpookyHashV2 instead -- it is faster, and produces
+ // a 64-bit hash, which means dramatically fewer collisions in large maps.
+ // (The above advice does not apply if you are targeting a 32-bit system.)
+ //
+ // Works only for Range<const char*> and Range<char*>
uint32_t hash() const {
// Taken from fbi/nstring.h:
// Quick and dirty bernstein hash...fine for short ascii strings
--e_;
}
- Range subpiece(size_type first,
- size_type length = std::string::npos) const {
+ Range subpiece(size_type first, size_type length = 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));
+
+ return Range(b_ + first, std::min(length, size() - first));
}
// string work-alike functions
return !empty() && back() == c;
}
+ /**
+ * Remove the items in [b, e), as long as this subrange is at the beginning
+ * or end of the Range.
+ *
+ * Required for boost::algorithm::trim()
+ */
+ void erase(Iter b, Iter e) {
+ if (b == b_) {
+ b_ = e;
+ } else if (e == e_) {
+ e_ = b;
+ } else {
+ throw std::out_of_range("index out of range");
+ }
+ }
+
/**
* Remove the given prefix and return true if the range starts with the given
* prefix; return false otherwise.
typedef Range<const unsigned char*> ByteRange;
typedef Range<unsigned char*> MutableByteRange;
-std::ostream& operator<<(std::ostream& os, const StringPiece& piece);
+inline std::ostream& operator<<(std::ostream& os,
+ const StringPiece piece) {
+ os.write(piece.start(), piece.size());
+ return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+ const MutableStringPiece piece) {
+ os.write(piece.start(), piece.size());
+ return os;
+}
/**
* Templated comparison operators
return StringPiece(lhs) >= StringPiece(rhs);
}
+// Do NOT use this, use SpookyHashV2 instead, see commment on hash() above.
struct StringPieceHash {
- std::size_t operator()(const StringPiece& str) const {
+ std::size_t operator()(const StringPiece str) const {
return static_cast<std::size_t>(str.hash());
}
};
namespace detail {
-size_t qfind_first_byte_of_nosse(const StringPiece& haystack,
- const StringPiece& needles);
-
-#if FOLLY_HAVE_EMMINTRIN_H && __GNUC_PREREQ(4, 6)
-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) {
+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>
}
};
-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) {
// specialization for StringPiece
template <>
inline size_t qfind(const Range<const char*>& haystack, const char& needle) {
+ // memchr expects a not-null pointer, early return if the range is empty.
+ if (haystack.empty()) {
+ return std::string::npos;
+ }
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) {
+ // memchr expects a not-null pointer, early return if the range is empty.
+ if (haystack.empty()) {
+ return std::string::npos;
+ }
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) {
+ // memchr expects a not-null pointer, early return if the range is empty.
+ if (haystack.empty()) {
+ return std::string::npos;
+ }
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) {
+ // memchr expects a not-null pointer, early return if the range is empty.
+ if (haystack.empty()) {
+ return std::string::npos;
+ }
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);
+ return qfind_first_of(haystack, needles, AsciiCaseSensitive());
}
// specialization for StringPiece
return detail::qfind_first_byte_of(StringPiece(haystack),
StringPiece(needles));
}
+
+template<class Key, class Enable>
+struct hasher;
+
+template <class T>
+struct hasher<folly::Range<T*>,
+ typename std::enable_if<std::is_pod<T>::value, void>::type> {
+ size_t operator()(folly::Range<T*> r) const {
+ return hash::SpookyHashV2::Hash64(r.begin(), r.size() * sizeof(T), 0);
+ }
+};
+
+/**
+ * Ubiquitous helper template for knowing what's a string
+ */
+template <class T> struct IsSomeString {
+ enum { value = std::is_same<T, std::string>::value
+ || std::is_same<T, fbstring>::value };
+};
+
} // !namespace folly
#pragma GCC diagnostic pop
FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(folly::Range);
-
-#endif // FOLLY_RANGE_H_