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