揭秘Facebook官方底层C++函数Folly
Facebook近日公布了其官方底层C++函数Folly,Folly(该缩略语表示Facebook开源代码库)其实是C++11组件库,这些组件在设计时着眼于实用性和高效率。
Folly与Boost、当然还有std等组件库的关系是互为补充,而不是彼此竞争。实际上,只有当我们需要的东西既没有,也无法满足所需的性能要求时,我们才开始定义自己的组件。
性能问题贯穿着Folly的大部分,有时导致比较具有特质性的设计(比如PackedSyncPtr.h和SmallLocks.h)。整体上确保良好的性能是所有Folly的统一主题。
逻辑设计
Folly是一组相对独立的组件的集合体,有些组件就是几个符号这么简单。内部依赖方面没有限制,这意味着某个特定的folly模块可以使用其他任何的folly组件。
所有符号都在顶层的命名空间folly中加以定义,当然除了宏。宏名称是ALL_UPPERCASE。命名空间folly定义了其他的内部命名空间,比如internal或detail。用户代码应该不依赖那些命名空间中的符号。
物理设计
在顶层,Folly采用经典的“结巴”(stuttering)方案folly/folly,这也是Boost及其他组件库所采用的。第一个目录充当库的安装根目录(可能是以folly-1.0/这样的形式);第二个目录是添加文件时用来辨别组件库,比如#include "folly/FBString.h"。
目录结构是扁平的(模仿命名空间结构),也就是说我们没有复杂的目录层次结构(这个情况在将来的版本中可能会有变化)。子目录experimental含有在folly里面使用的文件,可能用在Facebook端,但是被认为不够稳定,无法在客户端使用。你的代码不该使用folly/experimental中的文件,以免你在更新Folly时,出现问题。
folly/folly/test子目录包括了面向所有组件的单元测试,通常名为ComponentXyzTest.cpp,面向每个ComponentXyz.*。folly/folly/docs目录含有说明文档。
兼容性
目前,folly已在64位安装版Fedora 17、Ubuntu 12.04和Debian wheezy的gcc 4.6上进行了测试。它不用改动,就可以在其他64位Linux平台上运行。
组件
下面按字母顺序介绍了一系列Folly组件,另外附有每个组件的简短描述。
Arena.h,ThreadCachedArena.h
内存分配的简单地方:多次内存分配同时被释放。使用线程版本。
AtomicHashMap.h,AtomicHashArray.h
高性能的原子哈希图,采用几乎无锁的操作。
Benchmark.h
用于代码基准测试的小型框架。客户端代码注册基准测试,可选情况下使用一个变量来规定基准测试的范围(迭代和工作集大小等)。框架运行基准测试(受制于命令行标记),生成带计时信息的格式化输出。
Bits.h
各种位处理实用组件,针对速度而优化。
Bits.h
位变换函数,使用统一接口包装ffsl(l)图元。
ConcurrentSkipList.h
实现了用证实正确的可扩展并发跳跃表(Provably Correct Scalable Concurrent Skip List)描述的结构,这种跳跃表由Herlihy及其他人共同开发。
Conv.h
各种数据转换例程(尤其是to和from字符串),针对速度和安全进行了优化。
DiscriminatedPtr.h
类似boost::variant,但完全局限于指针。使用指针中最高位、未使用的16位作为鉴别器。所以sizeof(DiscriminatedPtr<int, string, Widget>) == sizeof(void*)。
dynamic.h
动态类型对象,创建时关注JSON对象。
Endian.h
Endian转换图元。
Escape.h
以C方式转义字符串。
eventfd.h
针对eventfd系统调用的包装器。
FBString.h
嵌入式实现std::string,进行了诸多优化。
FBVector.h
基本上嵌入式实现std::vector,进行了诸多优化。
Foreach.h
伪语句(作为宏语句来实现),用于迭代。
Format.h
Python式样的格式化实用组件。
GroupVarint.h
针对32位值的Group Varint编码。
Hash.h
各种流行的哈希函数实现。
Histogram.h
一个简单的类,用于收集直方图数据。
IntrusiveList.h
方便类型定义,用于使用boost::intrusive_list。
json.h
JSON序列化器和反序列化器。使用dynamic.h。
Likely.h
针对__builtin_expect的包装器。
Malloc.h
内存分配助手,尤其是使用jemalloc时。
MapUtil.h
一种助手,用于查找联合容器中的项目(比如std::map和std::unordered_map)。
PackedSyncPtr.h
一种高度专业化的数据结构,含有指针、1位旋转锁和15位整数,它们都在一个64位单词中。
Preprocessor.h
不好但又必不可少的组件。
PrettyPrint.h
针对数字的美化打印组件,用于添加所用单元的后缀:字节(kb、MB等)、度量单位后缀(k、M和G等)以及时间(s、ms、us和ns等)。
ProducerConsumerQueue.h
无锁单读取器单写入器队列。
Random.h
只定义了一个函数:randomNumberSeed()。
Range.h
Boost式样的范围工具和StringPiece专门化。
RWSpinLock.h
快速而紧凑的读取器/写入器旋转锁。
ScopeGuard.h
老式ScopeGuard用语的C++11版本。
SmallLocks.h
非常小的旋转锁(1字节和1位)。
small_vector.h
一种向量,含有小缓冲器方面的优化和可选的嵌入式PicoSpinLock。
sorted_vector_types.h
类似std::map的集合体,但是作为排序向量来实现。
StlAllocator.h
标准模板库(STL分配器),包装简单的分配/取消分配接口。
String.h
连接folly::fbstring和std::string的字符串实用组件。
Synchronized.h
高级同步库。
System.h
解码和errno实用组件。
ThreadCachedInt.h
使用线程缓存的高性能原子增量。
ThreadLocal.h
经过改进的线程本地存储,用于存储非平凡类型。
TimeoutQueue.h
按项目设定超时的队列。
Traits.h
类型特性,补充了在标准的C++11头<traits>中定义的那些类型特性。
Unicode.h
定义了codePointToUtf8函数。
来源:http://developer.51cto.com/art/201206/340607.htm
Facebook的c++开源库folly介绍
folly在Facebook内部广泛使用的一个c++库,是开源的,github地址:
https://github.com/facebook/folly
之前的boost库给我们带来了很多方便,比如智能指针之类的。
从c++11开始,对std标准库有了很大的补充,大大方便了c++程序员。
folly是Facebook open source library的缩写,其中用到了大量的C++11新标准,代码也是极其的规范,所以即使我们没必要使用folly这个库,我们也可以偶尔读一段它的源码,体验一下C++11带来的变化。
Conv.cpp
先贴一段代码,
/*
* 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.
* 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/Conv.h>
#include <array>
namespace folly {
namespace detail {
namespace {
/**
* Finds the first non-digit in a string. The number of digits
* searched depends on the precision of the Tgt integral. Assumes the
* string starts with NO whitespace and NO sign.
*
* The semantics of the routine is:
* for (;; ++b) {
* if (b >= e || !isdigit(*b)) return b;
* }
*
* Complete unrolling marks bottom-line (i.e. entire conversion)
* improvements of 20%.
*/
inline const char* findFirstNonDigit(const char* b, const char* e) {
for (; b < e; ++b) {
auto const c = static_cast<unsigned>(*b) - '0';
if (c >= 10) {
break;
}
}
return b;
}
// Maximum value of number when represented as a string
template <class T>
struct MaxString {
static const char* const value;
};
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.");
#if FOLLY_HAVE_INT128_T
template <> const char *const MaxString<__uint128_t>::value =
"340282366920938463463374607431768211455";
#endif
/*
* Lookup tables that converts from a decimal character value to an integral
* binary value, shifted by a decimal "shift" multiplier.
* For all character values in the range '0'..'9', the table at those
* index locations returns the actual decimal value shifted by the multiplier.
* For all other values, the lookup table returns an invalid OOR value.
*/
// Out-of-range flag value, larger than the largest value that can fit in
// four decimal bytes (9999), but four of these added up together should
// still not overflow uint16_t.
constexpr int32_t OOR = 10000;
FOLLY_ALIGNED(16) constexpr uint16_t shift1[] = {
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40
1, 2, 3, 4, 5, 6, 7, 8, 9, OOR, OOR,
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240
OOR, OOR, OOR, OOR, OOR, OOR // 250
};
FOLLY_ALIGNED(16) constexpr uint16_t shift10[] = {
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40
10, 20, 30, 40, 50, 60, 70, 80, 90, OOR, OOR,
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240
OOR, OOR, OOR, OOR, OOR, OOR // 250
};
FOLLY_ALIGNED(16) constexpr uint16_t shift100[] = {
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40
100, 200, 300, 400, 500, 600, 700, 800, 900, OOR, OOR,
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240
OOR, OOR, OOR, OOR, OOR, OOR // 250
};
FOLLY_ALIGNED(16) constexpr uint16_t shift1000[] = {
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40
1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, OOR, OOR,
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240
OOR, OOR, OOR, OOR, OOR, OOR // 250
};
struct ErrorString {
const char* string;
bool quote;
};
// Keep this in sync with ConversionCode in Conv.h
constexpr const std::array<
ErrorString,
static_cast<std::size_t>(ConversionCode::NUM_ERROR_CODES)>
kErrorStrings{{
{"Success", true},
{"Empty input string", true},
{"No digits found in input string", true},
{"Integer overflow when parsing bool (must be 0 or 1)", true},
{"Invalid value for bool", true},
{"Non-digit character found", true},
{"Invalid leading character", true},
{"Overflow during conversion", true},
{"Negative overflow during conversion", true},
{"Unable to convert string to floating point value", true},
{"Non-whitespace character found after end of conversion", true},
{"Overflow during arithmetic conversion", false},
{"Negative overflow during arithmetic conversion", false},
{"Loss of precision during arithmetic conversion", false},
}};
// Check if ASCII is really ASCII
using IsAscii = std::
integral_constant<bool, 'A' == 65 && 'Z' == 90 && 'a' == 97 && 'z' == 122>;
// The code in this file that uses tolower() really only cares about
// 7-bit ASCII characters, so we can take a nice shortcut here.
inline char tolower_ascii(char in) {
return IsAscii::value ? in | 0x20 : std::tolower(in);
}
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_ascii(*p) != *v) { // value is already lowercase
return false;
}
++p;
++v;
}
*b = p;
return true;
}
} // anonymous namespace
Expected<bool, ConversionCode> str_to_bool(StringPiece* src) noexcept {
auto b = src->begin(), e = src->end();
for (;; ++b) {
if (b >= e) {
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
}
if (!std::isspace(*b)) {
break;
}
}
bool result;
size_t len = e - b;
switch (*b) {
case '0':
case '1': {
result = false;
for (; b < e && isdigit(*b); ++b) {
if (result || (*b != '0' && *b != '1')) {
return makeUnexpected(ConversionCode::BOOL_OVERFLOW);
}
result = (*b == '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 {
return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE);
}
break;
default:
return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE);
}
src->assign(b, e);
return result;
}
/**
* StringPiece to double, with progress information. Alters the
* StringPiece parameter to munch the already-parsed characters.
*/
template <class Tgt>
Expected<Tgt, ConversionCode> str_to_floating(StringPiece* src) noexcept {
using namespace double_conversion;
static StringToDoubleConverter
conv(StringToDoubleConverter::ALLOW_TRAILING_JUNK
| StringToDoubleConverter::ALLOW_LEADING_SPACES,
0.0,
// return this for junk input string
std::numeric_limits<double>::quiet_NaN(),
nullptr, nullptr);
if (src->empty()) {
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
}
int length;
auto result = conv.StringToDouble(src->data(),
static_cast<int>(src->size()),
&length); // processed char count
if (!std::isnan(result)) {
// If we get here with length = 0, the input string is empty.
// If we get here with result = 0.0, it's either because the string
// contained only whitespace, or because we had an actual zero value
// (with potential trailing junk). If it was only whitespace, we
// want to raise an error; length will point past the last character
// that was processed, so we need to check if that character was
// whitespace or not.
if (length == 0 || (result == 0.0 && std::isspace((*src)[length - 1]))) {
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
}
src->advance(length);
return result;
}
auto* e = src->end();
auto* b =
std::find_if_not(src->begin(), e, [](char c) { return std::isspace(c); });
// There must be non-whitespace, otherwise we would have caught this above
assert(b < e);
size_t size = e - b;
bool negative = false;
if (*b == '-') {
negative = true;
++b;
--size;
}
result = 0.0;
switch (tolower_ascii(*b)) {
case 'i':
if (size >= 3 && tolower_ascii(b[1]) == 'n' &&
tolower_ascii(b[2]) == 'f') {
if (size >= 8 && tolower_ascii(b[3]) == 'i' &&
tolower_ascii(b[4]) == 'n' && tolower_ascii(b[5]) == 'i' &&
tolower_ascii(b[6]) == 't' && tolower_ascii(b[7]) == 'y') {
b += 8;
} else {
b += 3;
}
result = std::numeric_limits<Tgt>::infinity();
}
break;
case 'n':
if (size >= 3 && tolower_ascii(b[1]) == 'a' &&
tolower_ascii(b[2]) == 'n') {
b += 3;
result = std::numeric_limits<Tgt>::quiet_NaN();
}
break;
default:
break;
}
if (result == 0.0) {
// All bets are off
return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR);
}
if (negative) {
result = -result;
}
src->assign(b, e);
return result;
}
template Expected<float, ConversionCode> str_to_floating<float>(
StringPiece* src) noexcept;
template Expected<double, ConversionCode> str_to_floating<double>(
StringPiece* src) noexcept;
/**
* This class takes care of additional processing needed for signed values,
* like leading sign character and overflow checks.
*/
template <typename T, bool IsSigned = std::is_signed<T>::value>
class SignedValueHandler;
template <typename T>
class SignedValueHandler<T, true> {
public:
ConversionCode init(const char*& b) {
negative_ = false;
if (!std::isdigit(*b)) {
if (*b == '-') {
negative_ = true;
} else if (UNLIKELY(*b != '+')) {
return ConversionCode::INVALID_LEADING_CHAR;
}
++b;
}
return ConversionCode::SUCCESS;
}
ConversionCode overflow() {
return negative_ ? ConversionCode::NEGATIVE_OVERFLOW
: ConversionCode::POSITIVE_OVERFLOW;
}
template <typename U>
Expected<T, ConversionCode> finalize(U value) {
T rv;
if (negative_) {
rv = -value;
if (UNLIKELY(rv > 0)) {
return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
}
} else {
rv = value;
if (UNLIKELY(rv < 0)) {
return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);
}
}
return rv;
}
private:
bool negative_;
};
// For unsigned types, we don't need any extra processing
template <typename T>
class SignedValueHandler<T, false> {
public:
ConversionCode init(const char*&) {
return ConversionCode::SUCCESS;
}
ConversionCode overflow() {
return ConversionCode::POSITIVE_OVERFLOW;
}
Expected<T, ConversionCode> finalize(T value) {
return value;
}
};
/**
* String represented as a pair of pointers to char to signed/unsigned
* integrals. Assumes NO whitespace before or after, and also that the
* string is composed entirely of digits (and an optional sign only for
* signed types). String may be empty, in which case digits_to returns
* an appropriate error.
*/
template <class Tgt>
inline Expected<Tgt, ConversionCode> digits_to(
const char* b,
const char* const e) noexcept {
using UT = typename std::make_unsigned<Tgt>::type;
assert(b <= e);
SignedValueHandler<Tgt> sgn;
auto err = sgn.init(b);
if (UNLIKELY(err != ConversionCode::SUCCESS)) {
return makeUnexpected(err);
}
size_t size = e - b;
/* Although the string is entirely made of digits, we still need to
* check for overflow.
*/
if (size > std::numeric_limits<UT>::digits10) {
// Leading zeros?
if (b < e && *b == '0') {
for (++b;; ++b) {
if (b == e) {
return Tgt(0); // just zeros, e.g. "0000"
}
if (*b != '0') {
size = e - b;
break;
}
}
}
if (size > std::numeric_limits<UT>::digits10 &&
(size != std::numeric_limits<UT>::digits10 + 1 ||
strncmp(b, MaxString<UT>::value, size) > 0)) {
return makeUnexpected(sgn.overflow());
}
}
// Here we know that the number won't overflow when
// converted. Proceed without checks.
UT result = 0;
for (; e - b >= 4; b += 4) {
result *= 10000;
const int32_t r0 = shift1000[static_cast<size_t>(b[0])];
const int32_t r1 = shift100[static_cast<size_t>(b[1])];
const int32_t r2 = shift10[static_cast<size_t>(b[2])];
const int32_t r3 = shift1[static_cast<size_t>(b[3])];
const auto sum = r0 + r1 + r2 + r3;
if (sum >= OOR) {
goto outOfRange;
}
result += sum;
}
switch (e - b) {
case 3: {
const int32_t r0 = shift100[static_cast<size_t>(b[0])];
const int32_t r1 = shift10[static_cast<size_t>(b[1])];
const int32_t r2 = shift1[static_cast<size_t>(b[2])];
const auto sum = r0 + r1 + r2;
if (sum >= OOR) {
goto outOfRange;
}
result = 1000 * result + sum;
break;
}
case 2: {
const int32_t r0 = shift10[static_cast<size_t>(b[0])];
const int32_t r1 = shift1[static_cast<size_t>(b[1])];
const auto sum = r0 + r1;
if (sum >= OOR) {
goto outOfRange;
}
result = 100 * result + sum;
break;
}
case 1: {
const int32_t sum = shift1[static_cast<size_t>(b[0])];
if (sum >= OOR) {
goto outOfRange;
}
result = 10 * result + sum;
break;
}
default:
assert(b == e);
if (size == 0) {
return makeUnexpected(ConversionCode::NO_DIGITS);
}
break;
}
return sgn.finalize(result);
outOfRange:
return makeUnexpected(ConversionCode::NON_DIGIT_CHAR);
}
template Expected<char, ConversionCode> digits_to<char>(
const char*,
const char*) noexcept;
template Expected<signed char, ConversionCode> digits_to<signed char>(
const char*,
const char*) noexcept;
template Expected<unsigned char, ConversionCode> digits_to<unsigned char>(
const char*,
const char*) noexcept;
template Expected<short, ConversionCode> digits_to<short>(
const char*,
const char*) noexcept;
template Expected<unsigned short, ConversionCode> digits_to<unsigned short>(
const char*,
const char*) noexcept;
template Expected<int, ConversionCode> digits_to<int>(
const char*,
const char*) noexcept;
template Expected<unsigned int, ConversionCode> digits_to<unsigned int>(
const char*,
const char*) noexcept;
template Expected<long, ConversionCode> digits_to<long>(
const char*,
const char*) noexcept;
template Expected<unsigned long, ConversionCode> digits_to<unsigned long>(
const char*,
const char*) noexcept;
template Expected<long long, ConversionCode> digits_to<long long>(
const char*,
const char*) noexcept;
template Expected<unsigned long long, ConversionCode>
digits_to<unsigned long long>(const char*, const char*) noexcept;
#if FOLLY_HAVE_INT128_T
template Expected<__int128, ConversionCode> digits_to<__int128>(
const char*,
const char*) noexcept;
template Expected<unsigned __int128, ConversionCode>
digits_to<unsigned __int128>(const char*, const char*) noexcept;
#endif
/**
* StringPiece to integrals, with progress information. Alters the
* StringPiece parameter to munch the already-parsed characters.
*/
template <class Tgt>
Expected<Tgt, ConversionCode> str_to_integral(StringPiece* src) noexcept {
using UT = typename std::make_unsigned<Tgt>::type;
auto b = src->data(), past = src->data() + src->size();
for (;; ++b) {
if (UNLIKELY(b >= past)) {
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
}
if (!std::isspace(*b)) {
break;
}
}
SignedValueHandler<Tgt> sgn;
auto err = sgn.init(b);
if (UNLIKELY(err != ConversionCode::SUCCESS)) {
return makeUnexpected(err);
}
if (std::is_signed<Tgt>::value && UNLIKELY(b >= past)) {
return makeUnexpected(ConversionCode::NO_DIGITS);
}
if (UNLIKELY(!isdigit(*b))) {
return makeUnexpected(ConversionCode::NON_DIGIT_CHAR);
}
auto m = findFirstNonDigit(b + 1, past);
auto tmp = digits_to<UT>(b, m);
if (UNLIKELY(!tmp.hasValue())) {
return makeUnexpected(
tmp.error() == ConversionCode::POSITIVE_OVERFLOW ? sgn.overflow()
: tmp.error());
}
auto res = sgn.finalize(tmp.value());
if (res.hasValue()) {
src->advance(m - src->data());
}
return res;
}
template Expected<char, ConversionCode> str_to_integral<char>(
StringPiece* src) noexcept;
template Expected<signed char, ConversionCode> str_to_integral<signed char>(
StringPiece* src) noexcept;
template Expected<unsigned char, ConversionCode> str_to_integral<unsigned char>(
StringPiece* src) noexcept;
template Expected<short, ConversionCode> str_to_integral<short>(
StringPiece* src) noexcept;
template Expected<unsigned short, ConversionCode>
str_to_integral<unsigned short>(StringPiece* src) noexcept;
template Expected<int, ConversionCode> str_to_integral<int>(
StringPiece* src) noexcept;
template Expected<unsigned int, ConversionCode> str_to_integral<unsigned int>(
StringPiece* src) noexcept;
template Expected<long, ConversionCode> str_to_integral<long>(
StringPiece* src) noexcept;
template Expected<unsigned long, ConversionCode> str_to_integral<unsigned long>(
StringPiece* src) noexcept;
template Expected<long long, ConversionCode> str_to_integral<long long>(
StringPiece* src) noexcept;
template Expected<unsigned long long, ConversionCode>
str_to_integral<unsigned long long>(StringPiece* src) noexcept;
#if FOLLY_HAVE_INT128_T
template Expected<__int128, ConversionCode> str_to_integral<__int128>(
StringPiece* src) noexcept;
template Expected<unsigned __int128, ConversionCode>
str_to_integral<unsigned __int128>(StringPiece* src) noexcept;
#endif
} // namespace detail
ConversionError makeConversionError(ConversionCode code, StringPiece input) {
using namespace detail;
static_assert(
std::is_unsigned<std::underlying_type<ConversionCode>::type>::value,
"ConversionCode should be unsigned");
assert((std::size_t)code < kErrorStrings.size());
const ErrorString& err = kErrorStrings[(std::size_t)code];
if (code == ConversionCode::EMPTY_INPUT_STRING && input.empty()) {
return {err.string, code};
}
std::string tmp(err.string);
tmp.append(": ");
if (err.quote) {
tmp.append(1, '"');
}
if (input.size() > 0) {
tmp.append(input.data(), input.size());
}
if (err.quote) {
tmp.append(1, '"');
}
return {tmp, code};
}
} // namespace folly
我们看看上面的代码中使用了哪些c++11特性:
auto
static_assert
nullptr
initializer list