diff --git a/change/react-native-windows-a94bcf82-c2c3-467a-b720-c0903f97cbd9.json b/change/react-native-windows-a94bcf82-c2c3-467a-b720-c0903f97cbd9.json
new file mode 100644
index 00000000000..2804bb98de1
--- /dev/null
+++ b/change/react-native-windows-a94bcf82-c2c3-467a-b720-c0903f97cbd9.json
@@ -0,0 +1,7 @@
+{
+ "type": "prerelease",
+ "comment": "Forked folly for SDL compliance",
+ "packageName": "react-native-windows",
+ "email": "agnel@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/vnext/Folly/Folly.vcxproj b/vnext/Folly/Folly.vcxproj
index 83bb59d396e..655604ba9dc 100644
--- a/vnext/Folly/Folly.vcxproj
+++ b/vnext/Folly/Folly.vcxproj
@@ -256,7 +256,7 @@
$(ReactNativeWindowsDir)stubs;$(FollyDir);$(FmtDir)\include;%(AdditionalIncludeDirectories)
FOLLY_NO_CONFIG;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WINAPI_PARTITION_APP;%(PreprocessorDefinitions)
- 4244;4251;4267;4293;4305;4800;4804;4310;%(DisableSpecificWarnings)
+ 4251;4293;4305;4800;4804;4310;%(DisableSpecificWarnings)
/Zc:__cplusplus %(AdditionalOptions)
pch.h
diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/dynamic.cpp b/vnext/Folly/TEMP_UntilFollyUpdate/dynamic.cpp
new file mode 100644
index 00000000000..f145e2a4600
--- /dev/null
+++ b/vnext/Folly/TEMP_UntilFollyUpdate/dynamic.cpp
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * 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
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+namespace folly {
+
+//////////////////////////////////////////////////////////////////////
+
+#define FOLLY_DYNAMIC_DEF_TYPEINFO(T, str) \
+ const char* const dynamic::TypeInfo::name = str; \
+ //
+
+FOLLY_DYNAMIC_DEF_TYPEINFO(std::nullptr_t, "null")
+FOLLY_DYNAMIC_DEF_TYPEINFO(bool, "boolean")
+FOLLY_DYNAMIC_DEF_TYPEINFO(std::string, "string")
+FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array, "array")
+FOLLY_DYNAMIC_DEF_TYPEINFO(double, "double")
+FOLLY_DYNAMIC_DEF_TYPEINFO(int64_t, "int64")
+FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl, "object")
+
+#undef FOLLY_DYNAMIC_DEF_TYPEINFO
+
+const char* dynamic::typeName() const {
+ return typeName(type_);
+}
+
+TypeError::TypeError(const std::string& expected, dynamic::Type actual)
+ : std::runtime_error(sformat(
+ "TypeError: expected dynamic type `{}', but had type `{}'",
+ expected,
+ dynamic::typeName(actual))) {}
+
+TypeError::TypeError(
+ const std::string& expected, dynamic::Type actual1, dynamic::Type actual2)
+ : std::runtime_error(sformat(
+ "TypeError: expected dynamic types `{}, but had types `{}' and `{}'",
+ expected,
+ dynamic::typeName(actual1),
+ dynamic::typeName(actual2))) {}
+
+// This is a higher-order preprocessor macro to aid going from runtime
+// types to the compile time type system.
+#define FB_DYNAMIC_APPLY(type, apply) \
+ do { \
+ switch ((type)) { \
+ case NULLT: \
+ apply(std::nullptr_t); \
+ break; \
+ case ARRAY: \
+ apply(Array); \
+ break; \
+ case BOOL: \
+ apply(bool); \
+ break; \
+ case DOUBLE: \
+ apply(double); \
+ break; \
+ case INT64: \
+ apply(int64_t); \
+ break; \
+ case OBJECT: \
+ apply(ObjectImpl); \
+ break; \
+ case STRING: \
+ apply(std::string); \
+ break; \
+ default: \
+ CHECK(0); \
+ abort(); \
+ } \
+ } while (0)
+
+bool dynamic::operator<(dynamic const& o) const {
+ if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
+ throw_exception("object", type_);
+ }
+ if (type_ != o.type_) {
+ return type_ < o.type_;
+ }
+
+#define FB_X(T) return CompareOp::comp(*getAddress(), *o.getAddress())
+ FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+}
+
+bool dynamic::operator==(dynamic const& o) const {
+ if (type() != o.type()) {
+ if (isNumber() && o.isNumber()) {
+ auto& integ = isInt() ? *this : o;
+ auto& doubl = isInt() ? o : *this;
+ return integ.asInt() == doubl.asDouble();
+ }
+ return false;
+ }
+
+#define FB_X(T) return *getAddress() == *o.getAddress();
+ FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+}
+
+dynamic& dynamic::operator=(dynamic const& o) {
+ if (&o != this) {
+ if (type_ == o.type_) {
+#define FB_X(T) *getAddress() = *o.getAddress()
+ FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+ } else {
+ destroy();
+#define FB_X(T) new (getAddress()) T(*o.getAddress())
+ FB_DYNAMIC_APPLY(o.type_, FB_X);
+#undef FB_X
+ type_ = o.type_;
+ }
+ }
+ return *this;
+}
+
+dynamic& dynamic::operator=(dynamic&& o) noexcept {
+ if (&o != this) {
+ if (type_ == o.type_) {
+#define FB_X(T) *getAddress() = std::move(*o.getAddress())
+ FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+ } else {
+ destroy();
+#define FB_X(T) new (getAddress()) T(std::move(*o.getAddress()))
+ FB_DYNAMIC_APPLY(o.type_, FB_X);
+#undef FB_X
+ type_ = o.type_;
+ }
+ }
+ return *this;
+}
+
+dynamic const& dynamic::atImpl(dynamic const& idx) const& {
+ if (auto* parray = get_nothrow()) {
+ if (!idx.isInt()) {
+ throw_exception("int64", idx.type());
+ }
+ if (idx < 0 || idx >= parray->size()) {
+ throw_exception("out of range in dynamic array");
+ }
+ return (*parray)[size_t(idx.asInt())];
+ } else if (auto* pobject = get_nothrow()) {
+ auto it = pobject->find(idx);
+ if (it == pobject->end()) {
+ throw_exception(
+ sformat("couldn't find key {} in dynamic object", idx.asString()));
+ }
+ return it->second;
+ } else {
+ throw_exception("object/array", type());
+ }
+}
+
+dynamic const& dynamic::at(StringPiece idx) const& {
+ auto* pobject = get_nothrow();
+ if (!pobject) {
+ throw_exception("object", type());
+ }
+ auto it = pobject->find(idx);
+ if (it == pobject->end()) {
+ throw_exception(
+ sformat("couldn't find key {} in dynamic object", idx));
+ }
+ return it->second;
+}
+
+dynamic& dynamic::operator[](StringPiece k) & {
+ auto& obj = get();
+ auto ret = obj.emplace(k, nullptr);
+ return ret.first->second;
+}
+
+dynamic dynamic::getDefault(StringPiece k, const dynamic& v) const& {
+ auto& obj = get();
+ auto it = obj.find(k);
+ return it == obj.end() ? v : it->second;
+}
+
+dynamic dynamic::getDefault(StringPiece k, dynamic&& v) const& {
+ auto& obj = get();
+ auto it = obj.find(k);
+ // Avoid clang bug with ternary
+ if (it == obj.end()) {
+ return std::move(v);
+ } else {
+ return it->second;
+ }
+}
+
+dynamic dynamic::getDefault(StringPiece k, const dynamic& v) && {
+ auto& obj = get();
+ auto it = obj.find(k);
+ // Avoid clang bug with ternary
+ if (it == obj.end()) {
+ return v;
+ } else {
+ return std::move(it->second);
+ }
+}
+
+dynamic dynamic::getDefault(StringPiece k, dynamic&& v) && {
+ auto& obj = get();
+ auto it = obj.find(k);
+ return std::move(it == obj.end() ? v : it->second);
+}
+
+const dynamic* dynamic::get_ptrImpl(dynamic const& idx) const& {
+ if (auto* parray = get_nothrow()) {
+ if (!idx.isInt()) {
+ throw_exception("int64", idx.type());
+ }
+ if (idx < 0 || idx >= parray->size()) {
+ return nullptr;
+ }
+ return &(*parray)[size_t(idx.asInt())];
+ } else if (auto* pobject = get_nothrow()) {
+ auto it = pobject->find(idx);
+ if (it == pobject->end()) {
+ return nullptr;
+ }
+ return &it->second;
+ } else {
+ throw_exception("object/array", type());
+ }
+}
+
+const dynamic* dynamic::get_ptr(StringPiece idx) const& {
+ auto* pobject = get_nothrow();
+ if (!pobject) {
+ throw_exception("object", type());
+ }
+ auto it = pobject->find(idx);
+ if (it == pobject->end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+std::size_t dynamic::size() const {
+ if (auto* ar = get_nothrow()) {
+ return ar->size();
+ }
+ if (auto* obj = get_nothrow()) {
+ return obj->size();
+ }
+ if (auto* str = get_nothrow()) {
+ return str->size();
+ }
+ throw_exception("array/object/string", type());
+}
+
+dynamic::iterator dynamic::erase(const_iterator first, const_iterator last) {
+ auto& arr = get();
+ return get().erase(
+ arr.begin() + (first - arr.begin()), arr.begin() + (last - arr.begin()));
+}
+
+std::size_t dynamic::hash() const {
+ switch (type()) {
+ case NULLT:
+ return 0xBAAAAAAD;
+ case OBJECT: {
+ // Accumulate using addition instead of using hash_range (as in the ARRAY
+ // case), as we need a commutative hash operation since unordered_map's
+ // iteration order is unspecified.
+ auto h = std::hash>{};
+ return std::accumulate(
+ items().begin(),
+ items().end(),
+ size_t{0x0B1EC7},
+ [&](auto acc, auto const& item) { return acc + h(item); });
+ }
+ case ARRAY:
+ return static_cast(folly::hash::hash_range(begin(), end()));
+ case INT64:
+ return std::hash()(getInt());
+ case DOUBLE:
+ return std::hash()(getDouble());
+ case BOOL:
+ return std::hash()(getBool());
+ case STRING:
+ // keep consistent with detail::DynamicHasher
+ return Hash()(getString());
+ }
+ assume_unreachable();
+}
+
+char const* dynamic::typeName(Type t) {
+#define FB_X(T) return TypeInfo::name
+ FB_DYNAMIC_APPLY(t, FB_X);
+#undef FB_X
+}
+
+void dynamic::destroy() noexcept {
+ // This short-circuit speeds up some microbenchmarks.
+ if (type_ == NULLT) {
+ return;
+ }
+
+#define FB_X(T) detail::Destroy::destroy(getAddress())
+ FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+ type_ = NULLT;
+ u_.nul = nullptr;
+}
+
+dynamic dynamic::merge_diff(const dynamic& source, const dynamic& target) {
+ if (!source.isObject() || !target.isObject()) {
+ return target;
+ }
+
+ dynamic diff = object;
+
+ // added/modified keys
+ for (const auto& pair : target.items()) {
+ auto it = source.find(pair.first);
+ if (it == source.items().end()) {
+ diff[pair.first] = pair.second;
+ } else {
+ const auto& ssource = it->second;
+ const auto& starget = pair.second;
+ if (ssource.isObject() && starget.isObject()) {
+ auto sdiff = merge_diff(ssource, starget);
+ if (!sdiff.empty()) {
+ diff[pair.first] = std::move(sdiff);
+ }
+ } else if (ssource != starget) {
+ diff[pair.first] = merge_diff(ssource, starget);
+ }
+ }
+ }
+
+ // removed keys
+ for (const auto& pair : source.items()) {
+ auto it = target.find(pair.first);
+ if (it == target.items().end()) {
+ diff[pair.first] = nullptr;
+ }
+ }
+
+ return diff;
+}
+
+// clang-format off
+dynamic::resolved_json_pointer
+// clang-format on
+dynamic::try_get_ptr(json_pointer const& jsonPtr) const& {
+ using err_code = json_pointer_resolution_error_code;
+ using error = json_pointer_resolution_error;
+
+ auto const& tokens = jsonPtr.tokens();
+ if (tokens.empty()) {
+ return json_pointer_resolved_value{
+ nullptr, this, {nullptr, nullptr}, 0};
+ }
+
+ dynamic const* curr = this;
+ dynamic const* prev = nullptr;
+
+ size_t curr_idx{0};
+ StringPiece curr_key{};
+
+ for (auto it : enumerate(tokens)) {
+ // hit bottom but pointer not exhausted yet
+ if (!curr) {
+ return makeUnexpected(
+ error{err_code::json_pointer_out_of_bounds, it.index, prev});
+ }
+ prev = curr;
+ // handle lookup in array
+ if (auto const* parray = curr->get_nothrow()) {
+ if (it->size() > 1 && it->at(0) == '0') {
+ return makeUnexpected(
+ error{err_code::index_has_leading_zero, it.index, prev});
+ }
+ // if last element of pointer is '-', this is an append operation
+ if (it->size() == 1 && it->at(0) == '-') {
+ // was '-' the last token in pointer?
+ if (it.index == tokens.size() - 1) {
+ return makeUnexpected(
+ error{err_code::append_requested, it.index, prev});
+ }
+ // Cannot resolve past '-' in an array
+ curr = nullptr;
+ continue;
+ }
+ auto const idx = tryTo(*it);
+ if (!idx.hasValue()) {
+ return makeUnexpected(
+ error{err_code::index_not_numeric, it.index, prev});
+ }
+ if (idx.value() < parray->size()) {
+ curr = &(*parray)[idx.value()];
+ curr_idx = idx.value();
+ } else {
+ return makeUnexpected(
+ error{err_code::index_out_of_bounds, it.index, prev});
+ }
+ continue;
+ }
+ // handle lookup in object
+ if (auto const* pobject = curr->get_nothrow()) {
+ auto const sub_it = pobject->find(*it);
+ if (sub_it == pobject->end()) {
+ return makeUnexpected(error{err_code::key_not_found, it.index, prev});
+ }
+ curr = &sub_it->second;
+ curr_key = *it;
+ continue;
+ }
+ return makeUnexpected(
+ error{err_code::element_not_object_or_array, it.index, prev});
+ }
+ return json_pointer_resolved_value{
+ prev, curr, curr_key, curr_idx};
+}
+
+const dynamic* dynamic::get_ptr(json_pointer const& jsonPtr) const& {
+ using err_code = json_pointer_resolution_error_code;
+
+ auto ret = try_get_ptr(jsonPtr);
+ if (ret.hasValue()) {
+ return ret.value().value;
+ }
+
+ auto const ctx = ret.error().context;
+ auto const objType = ctx ? ctx->type() : Type::NULLT;
+
+ switch (ret.error().error_code) {
+ case err_code::key_not_found:
+ return nullptr;
+ case err_code::index_out_of_bounds:
+ return nullptr;
+ case err_code::append_requested:
+ return nullptr;
+ case err_code::index_not_numeric:
+ throw std::invalid_argument("array index is not numeric");
+ case err_code::index_has_leading_zero:
+ throw std::invalid_argument(
+ "leading zero not allowed when indexing arrays");
+ case err_code::element_not_object_or_array:
+ throw_exception("object/array", objType);
+ case err_code::json_pointer_out_of_bounds:
+ return nullptr;
+ case err_code::other:
+ default:
+ return nullptr;
+ }
+ assume_unreachable();
+}
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace folly
diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/lang/ToAscii.h b/vnext/Folly/TEMP_UntilFollyUpdate/lang/ToAscii.h
new file mode 100644
index 00000000000..1239feb9420
--- /dev/null
+++ b/vnext/Folly/TEMP_UntilFollyUpdate/lang/ToAscii.h
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if _MSC_VER
+#include
+#endif
+
+namespace folly {
+
+// to_ascii_alphabet
+//
+// Used implicity by to_ascii_lower and to_ascii_upper below.
+//
+// This alphabet translates digits to 0-9,a-z or 0-9,A-Z. The largest supported
+// base is 36; operator() presumes an argument less than that.
+//
+// Alternative alphabets may be used with to_ascii_with provided they match
+// the constructibility/destructibility and the interface of this one.
+template
+struct to_ascii_alphabet {
+ // operator()
+ //
+ // Translates a single digit to 0-9,a-z or 0-9,A-Z.
+ //
+ // async-signal-safe
+ constexpr char operator()(uint8_t b) const {
+ return b < 10 ? '0' + b : (Upper ? 'A' : 'a') + (b - 10);
+ }
+};
+using to_ascii_alphabet_lower = to_ascii_alphabet;
+using to_ascii_alphabet_upper = to_ascii_alphabet;
+
+namespace detail {
+
+#if defined(_M_IX86)
+FOLLY_ERASE auto to_ascii_port_clzll(uint64_t v) {
+ return __builtin_clzll(v);
+}
+#else
+FOLLY_ERASE auto to_ascii_port_clzll(uint64_t v) {
+#if _MSC_VER
+#if FOLLY_X64
+ return __lzcnt64(v);
+#else
+ return __assume(0), 0;
+#endif
+#else
+ return __builtin_clzll(v);
+#endif
+}
+#endif
+
+template
+struct to_ascii_array {
+ using data_type_ = c_array;
+ static constexpr data_type_ data_() {
+ data_type_ result{};
+ Alphabet alpha;
+ for (size_t i = 0; i < Base; ++i) {
+ result.data[i] = alpha(static_cast(i));
+ }
+ return result;
+ }
+ // @lint-ignore CLANGTIDY
+ static data_type_ const data;
+ constexpr char operator()(uint8_t index) const { // also an alphabet
+ return data.data[index];
+ }
+};
+template
+alignas(hardware_constructive_interference_size)
+ typename to_ascii_array::data_type_ const
+ to_ascii_array::data =
+ to_ascii_array::data_();
+
+extern template to_ascii_array<8, to_ascii_alphabet_lower>::data_type_ const
+ to_ascii_array<8, to_ascii_alphabet_lower>::data;
+extern template to_ascii_array<10, to_ascii_alphabet_lower>::data_type_ const
+ to_ascii_array<10, to_ascii_alphabet_lower>::data;
+extern template to_ascii_array<16, to_ascii_alphabet_lower>::data_type_ const
+ to_ascii_array<16, to_ascii_alphabet_lower>::data;
+extern template to_ascii_array<8, to_ascii_alphabet_upper>::data_type_ const
+ to_ascii_array<8, to_ascii_alphabet_upper>::data;
+extern template to_ascii_array<10, to_ascii_alphabet_upper>::data_type_ const
+ to_ascii_array<10, to_ascii_alphabet_upper>::data;
+extern template to_ascii_array<16, to_ascii_alphabet_upper>::data_type_ const
+ to_ascii_array<16, to_ascii_alphabet_upper>::data;
+
+template
+struct to_ascii_table {
+ using data_type_ = c_array;
+ static constexpr data_type_ data_() {
+ data_type_ result{};
+ Alphabet alpha;
+ for (size_t i = 0; i < Base * Base; ++i) {
+ result.data[i] = //
+ (alpha(uint8_t(i / Base)) << (kIsLittleEndian ? 0 : 8)) |
+ (alpha(uint8_t(i % Base)) << (kIsLittleEndian ? 8 : 0));
+ }
+ return result;
+ }
+ // @lint-ignore CLANGTIDY
+ static data_type_ const data;
+};
+template
+alignas(hardware_constructive_interference_size)
+ typename to_ascii_table::data_type_ const
+ to_ascii_table::data =
+ to_ascii_table::data_();
+
+extern template to_ascii_table<8, to_ascii_alphabet_lower>::data_type_ const
+ to_ascii_table<8, to_ascii_alphabet_lower>::data;
+extern template to_ascii_table<10, to_ascii_alphabet_lower>::data_type_ const
+ to_ascii_table<10, to_ascii_alphabet_lower>::data;
+extern template to_ascii_table<16, to_ascii_alphabet_lower>::data_type_ const
+ to_ascii_table<16, to_ascii_alphabet_lower>::data;
+extern template to_ascii_table<8, to_ascii_alphabet_upper>::data_type_ const
+ to_ascii_table<8, to_ascii_alphabet_upper>::data;
+extern template to_ascii_table<10, to_ascii_alphabet_upper>::data_type_ const
+ to_ascii_table<10, to_ascii_alphabet_upper>::data;
+extern template to_ascii_table<16, to_ascii_alphabet_upper>::data_type_ const
+ to_ascii_table<16, to_ascii_alphabet_upper>::data;
+
+template
+struct to_ascii_powers {
+ static constexpr size_t size_(I v) {
+ return 1 + (v < Base ? 0 : size_(v / Base));
+ }
+ static constexpr size_t const size = size_(~I(0));
+ using data_type_ = c_array;
+ static constexpr data_type_ data_() {
+ data_type_ result{};
+ for (size_t i = 0; i < size; ++i) {
+ result.data[i] = constexpr_pow(Base, i);
+ }
+ return result;
+ }
+ // @lint-ignore CLANGTIDY
+ static data_type_ const data;
+};
+template
+constexpr size_t const to_ascii_powers::size;
+template
+alignas(hardware_constructive_interference_size)
+ typename to_ascii_powers::data_type_ const
+ to_ascii_powers::data = to_ascii_powers::data_();
+
+extern template to_ascii_powers<8, uint64_t>::data_type_ const
+ to_ascii_powers<8, uint64_t>::data;
+extern template to_ascii_powers<10, uint64_t>::data_type_ const
+ to_ascii_powers<10, uint64_t>::data;
+extern template to_ascii_powers<16, uint64_t>::data_type_ const
+ to_ascii_powers<16, uint64_t>::data;
+
+template
+FOLLY_ALWAYS_INLINE size_t to_ascii_size_imuls(uint64_t v) {
+ using powers = to_ascii_powers;
+ uint64_t p = 1;
+ for (size_t i = 0u; i < powers::size; ++i, p *= Base) {
+ if (FOLLY_UNLIKELY(v < p)) {
+ return i + size_t(i == 0);
+ }
+ }
+ return powers::size;
+}
+
+template
+FOLLY_ALWAYS_INLINE size_t to_ascii_size_idivs(uint64_t v) {
+ size_t i = 1;
+ while (v >= Base) {
+ i += 1;
+ v /= Base;
+ }
+ return i;
+}
+
+template
+FOLLY_ALWAYS_INLINE size_t to_ascii_size_array(uint64_t v) {
+ using powers = to_ascii_powers;
+ for (size_t i = 0u; i < powers::size; ++i) {
+ if (FOLLY_LIKELY(v < powers::data.data[i])) {
+ return i + size_t(i == 0);
+ }
+ }
+ return powers::size;
+}
+
+// For some architectures, we can get a little help from clzll, the "count
+// leading zeros" builtin, which is backed by a single performant instruction.
+//
+// Note that the compiler implements __builtin_clzll on all architectures, but
+// only emits a single clzll instruction when the architecture has one.
+//
+// This implementation may be faster than the basic ones in the general case
+// because the time taken to compute this one is constant for non-zero v,
+// whereas the basic ones take time proportional to log<2>(v). Whether this one
+// is actually faster depends on the emitted code for this implementation and
+// on whether the loops in the basic implementations are unrolled.
+template
+FOLLY_ALWAYS_INLINE size_t to_ascii_size_clzll(uint64_t v) {
+ using powers = to_ascii_powers;
+
+ // clzll is undefined for 0; must special case this
+ if (FOLLY_UNLIKELY(!v)) {
+ return 1;
+ }
+
+ // log2 is approx log<2>(v)
+ size_t const vlog2 = 64 - static_cast(to_ascii_port_clzll(v));
+
+ // handle directly when Base is power-of-two
+ if (!(Base & (Base - 1))) {
+ constexpr auto const blog2 = constexpr_log2(Base);
+ return vlog2 / blog2 + size_t(vlog2 % blog2 != 0);
+ }
+
+ // blog2r is approx 1 / log<2>(Base), used in log change-of-base just below
+ constexpr auto const blog2r = 8. / constexpr_log2(constexpr_pow(Base, 8));
+
+ // vlogb is approx log(v) = log<2>(v) / log<2>(Base)
+ auto const vlogb = vlog2 * size_t(blog2r * 256) / 256;
+
+ // return vlogb, adjusted if necessary
+ return vlogb + size_t(vlogb < powers::size && v >= powers::data.data[vlogb]);
+}
+
+template
+FOLLY_ALWAYS_INLINE size_t to_ascii_size(uint64_t v) {
+ return kIsArchAmd64 && !(Base & (Base - 1)) //
+ ? to_ascii_size_clzll(v)
+ : to_ascii_size_array(v);
+}
+
+// The straightforward implementation, assuming the size known in advance.
+//
+// The straightforward implementation without the size known in advance would
+// entail emitting the bytes backward and then reversing them at the end, once
+// the size is known.
+template
+FOLLY_ALWAYS_INLINE void to_ascii_with_basic(
+ char* out, size_t size, uint64_t v) {
+ Alphabet const xlate;
+ for (auto pos = size - 1; pos; --pos) {
+ // keep /, % together so a peephole optimization computes them together
+ auto const q = v / Base;
+ auto const r = v % Base;
+ out[pos] = xlate(r);
+ v = q;
+ }
+ out[0] = xlate(v);
+}
+template
+FOLLY_ALWAYS_INLINE size_t to_ascii_with_basic(char* out, uint64_t v) {
+ auto const size = to_ascii_size(v);
+ to_ascii_with_basic(out, size, v);
+ return size;
+}
+
+// A variant of the straightforward implementation, but using a lookup table.
+template
+FOLLY_ALWAYS_INLINE void to_ascii_with_array(
+ char* out, size_t size, uint64_t v) {
+ using array = to_ascii_array; // also an alphabet
+ to_ascii_with_basic(out, size, v);
+}
+template
+FOLLY_ALWAYS_INLINE size_t to_ascii_with_array(char* out, uint64_t v) {
+ auto const size = to_ascii_size(v);
+ to_ascii_with_array(out, size, v);
+ return size;
+}
+
+// A trickier implementation which performs half as many divides as the other,
+// more straightforward, implementation. On modern hardware, the divides are
+// the bottleneck (even when the compiler emits a complicated sequence of add,
+// sub, and mul instructions with special constants to simulate a divide by a
+// fixed denominator).
+//
+// The downside of this implementation is that the emitted code is larger,
+// especially when the divide is simulated, which affects inlining decisions.
+template
+FOLLY_ALWAYS_INLINE void to_ascii_with_table(
+ char* out, size_t size, uint64_t v) {
+ using table = to_ascii_table;
+ auto pos = size - 2;
+ while (FOLLY_UNLIKELY(v >= Base * Base)) {
+ // keep /, % together so a peephole optimization computes them together
+ auto const q = v / (Base * Base);
+ auto const r = v % (Base * Base);
+ auto const val = table::data.data[size_t(r)];
+ std::memcpy(out + pos, &val, 2);
+ pos -= 2;
+ v = q;
+ }
+ auto const val = table::data.data[size_t(v)];
+ if (FOLLY_UNLIKELY(size % 2 == 0)) {
+ std::memcpy(out, &val, 2);
+ } else {
+ *out = val >> (kIsLittleEndian ? 8 : 0);
+ }
+}
+template
+FOLLY_ALWAYS_INLINE size_t to_ascii_with_table(char* out, uint64_t v) {
+ auto const size = to_ascii_size(v);
+ to_ascii_with_table(out, size, v);
+ return size;
+}
+
+} // namespace detail
+
+// to_ascii_size_max
+//
+// The maximum size buffer that might be required to hold the ascii-encoded
+// representation of any value of unsigned type I in base Base.
+//
+// In base 10, u64 requires at most 20 bytes, u32 at most 10, u16 at most 5,
+// and u8 at most 3.
+//
+// async-signal-safe
+template
+constexpr size_t to_ascii_size_max() {
+ return detail::to_ascii_powers::size;
+}
+
+// to_ascii_size_max_decimal
+//
+// An alias to to_ascii_size_max<10>.
+//
+// async-signal-safe
+template
+constexpr size_t to_ascii_size_max_decimal() {
+ return to_ascii_size_max<10, I>();
+}
+
+// to_ascii_size
+//
+// Returns the number of digits in the base Base representation of a uint64_t.
+// Useful for preallocating buffers, etc.
+//
+// async-signal-safe
+template
+size_t to_ascii_size(uint64_t v) {
+ return detail::to_ascii_size(v);
+}
+
+// to_ascii_size_decimal
+//
+// An alias to to_ascii_size<10>.
+//
+// async-signal-safe
+inline size_t to_ascii_size_decimal(uint64_t v) {
+ return to_ascii_size<10>(v);
+}
+
+// to_ascii_with
+//
+// Copies the digits of v, in base Base, translated with Alphabet, into buffer
+// and returns the number of bytes written.
+//
+// Does *not* append a null terminator. It is the caller's responsibility to
+// append a null terminator if one is required.
+//
+// Assumes buffer points to at least to_ascii_size(v) bytes of writable
+// memory. It is the caller's responsibility to provide a writable buffer with
+// the required min size.
+//
+// async-signal-safe
+template
+size_t to_ascii_with(char* out, uint64_t v) {
+ return detail::to_ascii_with_table(out, v);
+}
+
+// to_ascii_lower
+//
+// Composes to_ascii_with with to_ascii_alphabet_lower.
+//
+// async-signal-safe
+template
+size_t to_ascii_lower(char* out, uint64_t v) {
+ return to_ascii_with(out, v);
+}
+
+// to_ascii_upper
+//
+// Composes to_ascii_with with to_ascii_alphabet_upper.
+//
+// async-signal-safe
+template
+size_t to_ascii_upper(char* out, uint64_t v) {
+ return to_ascii_with(out, v);
+}
+
+// to_ascii_decimal
+//
+// An alias to to_ascii<10, false>.
+//
+// async-signals-afe
+inline size_t to_ascii_decimal(char* out, uint64_t v) {
+ return to_ascii_lower<10>(out, v);
+}
+
+} // namespace folly