Isolate TokenSpec such that it doesn't depend on flow headers
This commit is contained in:
parent
b28cb708a2
commit
03f0696a4b
|
@ -139,15 +139,6 @@ namespace authz {
|
|||
|
||||
using MessageDigestMethod = const EVP_MD*;
|
||||
|
||||
Algorithm algorithmFromString(StringRef s) noexcept {
|
||||
if (s == "RS256"_sr)
|
||||
return Algorithm::RS256;
|
||||
else if (s == "ES256"_sr)
|
||||
return Algorithm::ES256;
|
||||
else
|
||||
return Algorithm::UNKNOWN;
|
||||
}
|
||||
|
||||
std::pair<PKeyAlgorithm, MessageDigestMethod> getMethod(Algorithm alg) {
|
||||
if (alg == Algorithm::RS256) {
|
||||
return { PKeyAlgorithm::RSA, ::EVP_sha256() };
|
||||
|
@ -169,36 +160,6 @@ std::string_view getAlgorithmName(Algorithm alg) {
|
|||
|
||||
} // namespace authz
|
||||
|
||||
namespace authz::flatbuffers {
|
||||
|
||||
SignedTokenRef signToken(Arena& arena, TokenRef token, StringRef keyName, PrivateKey privateKey) {
|
||||
auto ret = SignedTokenRef{};
|
||||
auto writer = ObjectWriter([&arena](size_t len) { return new (arena) uint8_t[len]; }, IncludeVersion());
|
||||
writer.serialize(token);
|
||||
auto tokenStr = writer.toStringRef();
|
||||
auto sig = privateKey.sign(arena, tokenStr, *::EVP_sha256());
|
||||
ret.token = tokenStr;
|
||||
ret.signature = sig;
|
||||
ret.keyName = StringRef(arena, keyName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool verifyToken(SignedTokenRef signedToken, PublicKey publicKey) {
|
||||
return publicKey.verify(signedToken.token, signedToken.signature, *::EVP_sha256());
|
||||
}
|
||||
|
||||
TokenRef makeRandomTokenSpec(Arena& arena, IRandom& rng) {
|
||||
auto token = TokenRef{};
|
||||
token.expiresAt = timer_monotonic() * (0.5 + rng.random01());
|
||||
const auto numTenants = rng.randomInt(1, 3);
|
||||
for (auto i = 0; i < numTenants; i++) {
|
||||
token.tenants.push_back(arena, genRandomAlphanumStringRef(arena, rng, MinTenantNameLen, MaxTenantNameLenPlus1));
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
} // namespace authz::flatbuffers
|
||||
|
||||
namespace authz::jwt {
|
||||
|
||||
template <class FieldType, size_t NameLen>
|
||||
|
@ -378,7 +339,7 @@ Optional<StringRef> parseHeaderPart(Arena& arena, TokenRef& token, StringRef b64
|
|||
if (typValue != "JWT"_sr)
|
||||
return "'typ' is not 'JWT'"_sr;
|
||||
auto algValue = StringRef(reinterpret_cast<const uint8_t*>(alg.GetString()), alg.GetStringLength());
|
||||
auto algType = algorithmFromString(algValue);
|
||||
auto algType = algorithmFromString(algValue.toStringView());
|
||||
if (algType == Algorithm::UNKNOWN)
|
||||
return "Unsupported algorithm"_sr;
|
||||
token.algorithm = algType;
|
||||
|
@ -582,29 +543,6 @@ TokenRef makeRandomTokenSpec(Arena& arena, IRandom& rng, Algorithm alg) {
|
|||
|
||||
void forceLinkTokenSignTests() {}
|
||||
|
||||
TEST_CASE("/fdbrpc/TokenSign/FlatBuffer") {
|
||||
const auto numIters = 100;
|
||||
for (auto i = 0; i < numIters; i++) {
|
||||
auto arena = Arena();
|
||||
auto privateKey = mkcert::makeEcP256();
|
||||
auto publicKey = privateKey.toPublic();
|
||||
auto& rng = *deterministicRandom();
|
||||
auto tokenSpec = authz::flatbuffers::makeRandomTokenSpec(arena, rng);
|
||||
auto keyName = genRandomAlphanumStringRef(arena, rng, MinKeyNameLen, MaxKeyNameLenPlus1);
|
||||
auto signedToken = authz::flatbuffers::signToken(arena, tokenSpec, keyName, privateKey);
|
||||
ASSERT(authz::flatbuffers::verifyToken(signedToken, publicKey));
|
||||
// try tampering with signed token by adding one more tenant
|
||||
tokenSpec.tenants.push_back(arena,
|
||||
genRandomAlphanumStringRef(arena, rng, MinTenantNameLen, MaxTenantNameLenPlus1));
|
||||
auto writer = ObjectWriter([&arena](size_t len) { return new (arena) uint8_t[len]; }, IncludeVersion());
|
||||
writer.serialize(tokenSpec);
|
||||
signedToken.token = writer.toStringRef();
|
||||
ASSERT(!authz::flatbuffers::verifyToken(signedToken, publicKey));
|
||||
}
|
||||
printf("%d runs OK\n", numIters);
|
||||
return Void();
|
||||
}
|
||||
|
||||
TEST_CASE("/fdbrpc/TokenSign/JWT") {
|
||||
const auto numIters = 100;
|
||||
for (auto i = 0; i < numIters; i++) {
|
||||
|
@ -685,73 +623,42 @@ TEST_CASE("/fdbrpc/TokenSign/JWT/ToStringRef") {
|
|||
return Void();
|
||||
}
|
||||
|
||||
// This unit test takes too long to run in RandomUnitTests.toml
|
||||
// FIXME: Move this to benchmark to flowbench
|
||||
/*
|
||||
TEST_CASE("/fdbrpc/TokenSign/bench") {
|
||||
auto keyTypes = std::array<StringRef, 2>{ "EC"_sr, "RSA"_sr };
|
||||
for (auto kty : keyTypes) {
|
||||
constexpr auto repeat = 5;
|
||||
constexpr auto numSamples = 10000;
|
||||
fmt::print("=== {} keys case\n", kty.toString());
|
||||
auto key = kty == "EC"_sr ? mkcert::makeEcP256() : mkcert::makeRsa4096Bit();
|
||||
auto pubKey = key.toPublic();
|
||||
auto& rng = *deterministicRandom();
|
||||
auto arena = Arena();
|
||||
auto jwtSpecs = new (arena) authz::jwt::TokenRef[numSamples];
|
||||
auto fbSpecs = new (arena) authz::flatbuffers::TokenRef[numSamples];
|
||||
auto jwts = new (arena) StringRef[numSamples];
|
||||
auto fbs = new (arena) StringRef[numSamples];
|
||||
for (auto i = 0; i < numSamples; i++) {
|
||||
jwtSpecs[i] = authz::jwt::makeRandomTokenSpec(
|
||||
arena, rng, kty == "EC"_sr ? authz::Algorithm::ES256 : authz::Algorithm::RS256);
|
||||
fbSpecs[i] = authz::flatbuffers::makeRandomTokenSpec(arena, rng);
|
||||
}
|
||||
{
|
||||
auto const jwtSignBegin = timer_monotonic();
|
||||
for (auto i = 0; i < numSamples; i++) {
|
||||
jwts[i] = authz::jwt::signToken(arena, jwtSpecs[i], key);
|
||||
}
|
||||
auto const jwtSignEnd = timer_monotonic();
|
||||
fmt::print("JWT Sign : {:.2f} OPS\n", numSamples / (jwtSignEnd - jwtSignBegin));
|
||||
}
|
||||
{
|
||||
auto const jwtVerifyBegin = timer_monotonic();
|
||||
for (auto rep = 0; rep < repeat; rep++) {
|
||||
for (auto i = 0; i < numSamples; i++) {
|
||||
auto verifyOk = authz::jwt::verifyToken(jwts[i], pubKey);
|
||||
ASSERT(verifyOk);
|
||||
}
|
||||
}
|
||||
auto const jwtVerifyEnd = timer_monotonic();
|
||||
fmt::print("JWT Verify : {:.2f} OPS\n", repeat * numSamples / (jwtVerifyEnd - jwtVerifyBegin));
|
||||
}
|
||||
{
|
||||
auto tmpArena = Arena();
|
||||
auto const fbSignBegin = timer_monotonic();
|
||||
for (auto i = 0; i < numSamples; i++) {
|
||||
auto fbToken = authz::flatbuffers::signToken(tmpArena, fbSpecs[i], "defaultKey"_sr, key);
|
||||
auto wr = ObjectWriter([&arena](size_t len) { return new (arena) uint8_t[len]; }, Unversioned());
|
||||
wr.serialize(fbToken);
|
||||
fbs[i] = wr.toStringRef();
|
||||
}
|
||||
auto const fbSignEnd = timer_monotonic();
|
||||
fmt::print("FlatBuffers Sign : {:.2f} OPS\n", numSamples / (fbSignEnd - fbSignBegin));
|
||||
}
|
||||
{
|
||||
auto const fbVerifyBegin = timer_monotonic();
|
||||
for (auto rep = 0; rep < repeat; rep++) {
|
||||
for (auto i = 0; i < numSamples; i++) {
|
||||
auto signedToken = ObjectReader::fromStringRef<Standalone<authz::flatbuffers::SignedTokenRef>>(
|
||||
fbs[i], Unversioned());
|
||||
auto verifyOk = authz::flatbuffers::verifyToken(signedToken, pubKey);
|
||||
ASSERT(verifyOk);
|
||||
}
|
||||
}
|
||||
auto const fbVerifyEnd = timer_monotonic();
|
||||
fmt::print("FlatBuffers Verify : {:.2f} OPS\n", repeat * numSamples / (fbVerifyEnd - fbVerifyBegin));
|
||||
}
|
||||
}
|
||||
return Void();
|
||||
auto keyTypes = std::array<StringRef, 1>{ "EC"_sr };
|
||||
for (auto kty : keyTypes) {
|
||||
constexpr auto repeat = 5;
|
||||
constexpr auto numSamples = 10000;
|
||||
fmt::print("=== {} keys case\n", kty.toString());
|
||||
auto key = kty == "EC"_sr ? mkcert::makeEcP256() : mkcert::makeRsa4096Bit();
|
||||
auto pubKey = key.toPublic();
|
||||
auto& rng = *deterministicRandom();
|
||||
auto arena = Arena();
|
||||
auto jwtSpecs = new (arena) authz::jwt::TokenRef[numSamples];
|
||||
auto jwts = new (arena) StringRef[numSamples];
|
||||
for (auto i = 0; i < numSamples; i++) {
|
||||
jwtSpecs[i] = authz::jwt::makeRandomTokenSpec(
|
||||
arena, rng, kty == "EC"_sr ? authz::Algorithm::ES256 : authz::Algorithm::RS256);
|
||||
}
|
||||
{
|
||||
auto const jwtSignBegin = timer_monotonic();
|
||||
for (auto i = 0; i < numSamples; i++) {
|
||||
jwts[i] = authz::jwt::signToken(arena, jwtSpecs[i], key);
|
||||
}
|
||||
auto const jwtSignEnd = timer_monotonic();
|
||||
fmt::print("JWT Sign : {:.2f} OPS\n", numSamples / (jwtSignEnd - jwtSignBegin));
|
||||
}
|
||||
{
|
||||
auto const jwtVerifyBegin = timer_monotonic();
|
||||
for (auto rep = 0; rep < repeat; rep++) {
|
||||
for (auto i = 0; i < numSamples; i++) {
|
||||
auto [verifyOk, errorMsg] = authz::jwt::verifyToken(jwts[i], pubKey);
|
||||
ASSERT(!errorMsg.present());
|
||||
ASSERT(verifyOk);
|
||||
}
|
||||
}
|
||||
auto const jwtVerifyEnd = timer_monotonic();
|
||||
fmt::print("JWT Verify : {:.2f} OPS\n", repeat * numSamples / (jwtVerifyEnd - jwtVerifyBegin));
|
||||
}
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "fdbrpc/TokenSign.h"
|
||||
#include "fdbrpc/TokenSignStdTypes.h"
|
||||
#include "flow/PKey.h"
|
||||
#include "flow/MkCert.h"
|
||||
|
|
|
@ -18,94 +18,21 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef FDBRPC_TOKEN_SIGN_H
|
||||
#define FDBRPC_TOKEN_SIGN_H
|
||||
#pragma once
|
||||
|
||||
#include "flow/network.h"
|
||||
#include "flow/Arena.h"
|
||||
#include "flow/FileIdentifier.h"
|
||||
#include "flow/PKey.h"
|
||||
#include "fdbrpc/TokenSpec.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace authz {
|
||||
|
||||
enum class Algorithm : int {
|
||||
RS256,
|
||||
ES256,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
Algorithm algorithmFromString(StringRef s) noexcept;
|
||||
|
||||
} // namespace authz
|
||||
|
||||
namespace authz::flatbuffers {
|
||||
|
||||
struct TokenRef {
|
||||
static constexpr FileIdentifier file_identifier = 1523118;
|
||||
double expiresAt;
|
||||
VectorRef<StringRef> tenants;
|
||||
|
||||
template <class Ar>
|
||||
void serialize(Ar& ar) {
|
||||
serializer(ar, expiresAt, tenants);
|
||||
}
|
||||
};
|
||||
|
||||
struct SignedTokenRef {
|
||||
static constexpr FileIdentifier file_identifier = 5916732;
|
||||
StringRef token;
|
||||
StringRef keyName;
|
||||
StringRef signature;
|
||||
|
||||
template <class Ar>
|
||||
void serialize(Ar& ar) {
|
||||
serializer(ar, token, keyName, signature);
|
||||
}
|
||||
|
||||
int expectedSize() const { return token.size() + keyName.size() + signature.size(); }
|
||||
};
|
||||
|
||||
SignedTokenRef signToken(Arena& arena, TokenRef token, StringRef keyName, PrivateKey privateKey);
|
||||
|
||||
bool verifyToken(SignedTokenRef signedToken, PublicKey publicKey);
|
||||
|
||||
} // namespace authz::flatbuffers
|
||||
|
||||
namespace authz::jwt {
|
||||
|
||||
// Given S = concat(B64UrlEnc(headerJson), ".", B64UrlEnc(payloadJson)),
|
||||
// JWT is concat(S, ".", B64UrlEnc(sign(S, PrivateKey))).
|
||||
// Below we refer to S as "sign input"
|
||||
|
||||
// This struct is not meant to be flatbuffer-serialized
|
||||
// This is a parsed, flattened view of S and signature
|
||||
template <bool IsArenaBased>
|
||||
struct BasicTokenSpec {
|
||||
using StringType = std::conditional_t<IsArenaBased, StringRef, std::string>;
|
||||
template <class T>
|
||||
using VectorType = std::conditional_t<IsArenaBased, VectorRef<T>, std::vector<T>>;
|
||||
template <class T>
|
||||
using OptionalType = std::conditional_t<IsArenaBased, Optional<T>, std::optional<T>>;
|
||||
// header part ("typ": "JWT" implicitly enforced)
|
||||
Algorithm algorithm; // alg
|
||||
StringType keyId; // kid
|
||||
// payload part
|
||||
OptionalType<StringType> issuer; // iss
|
||||
OptionalType<StringType> subject; // sub
|
||||
OptionalType<VectorType<StringType>> audience; // aud
|
||||
OptionalType<uint64_t> issuedAtUnixTime; // iat
|
||||
OptionalType<uint64_t> expiresAtUnixTime; // exp
|
||||
OptionalType<uint64_t> notBeforeUnixTime; // nbf
|
||||
OptionalType<StringType> tokenId; // jti
|
||||
OptionalType<VectorType<StringType>> tenants; // tenants
|
||||
// signature part
|
||||
StringType signature;
|
||||
};
|
||||
|
||||
using TokenRef = BasicTokenSpec<true>;
|
||||
using TokenRef = BasicTokenSpec<StringRef, VectorRef, Optional>;
|
||||
|
||||
// print each non-signature field in non-JSON, human-readable format e.g. for trace
|
||||
StringRef toStringRef(Arena& arena, const TokenRef& tokenSpec);
|
||||
|
|
|
@ -20,8 +20,9 @@
|
|||
#pragma once
|
||||
#ifndef FDBRPC_TOKEN_SIGN_STD_TYPES_H
|
||||
#define FDBRPC_TOKEN_SIGN_STD_TYPES_H
|
||||
#include "fdbrpc/TokenSign.h"
|
||||
#include "fdbrpc/TokenSpec.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Below functions build as a library separate from fdbrpc
|
||||
// The intent is to re-use the key/token generation part in a way that the input, the output,
|
||||
|
@ -30,7 +31,7 @@
|
|||
|
||||
namespace authz::jwt::stdtypes {
|
||||
|
||||
using TokenSpec = BasicTokenSpec<false /*IsArenaBased*/>;
|
||||
using TokenSpec = BasicTokenSpec<std::string, std::vector>;
|
||||
|
||||
// Generate an elliptic curve private key on a P-256 curve, and serialize it as PEM.
|
||||
std::string makeEcP256PrivateKeyPem();
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* TokenSpec.h
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* 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 FDBRPC_TOKEN_SPEC_H
|
||||
#define FDBRPC_TOKEN_SPEC_H
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace authz {
|
||||
|
||||
enum class Algorithm : int {
|
||||
RS256,
|
||||
ES256,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
inline Algorithm algorithmFromString(std::string_view s) noexcept {
|
||||
if (s == "RS256")
|
||||
return Algorithm::RS256;
|
||||
else if (s == "ES256")
|
||||
return Algorithm::ES256;
|
||||
else
|
||||
return Algorithm::UNKNOWN;
|
||||
}
|
||||
|
||||
} // namespace authz
|
||||
|
||||
namespace authz::jwt {
|
||||
|
||||
// Given S = concat(B64UrlEnc(headerJson), ".", B64UrlEnc(payloadJson)),
|
||||
// JWT is concat(S, ".", B64UrlEnc(sign(S, PrivateKey))).
|
||||
// Below we refer to S as "sign input"
|
||||
|
||||
// This struct is not meant to be flatbuffer-serialized
|
||||
// This is a parsed, flattened view of S and signature
|
||||
|
||||
template <class StringType, template <class> class VectorType, template <class> class OptionalType = std::optional>
|
||||
struct BasicTokenSpec {
|
||||
// header part ("typ": "JWT" implicitly enforced)
|
||||
Algorithm algorithm; // alg
|
||||
StringType keyId; // kid
|
||||
// payload part
|
||||
OptionalType<StringType> issuer; // iss
|
||||
OptionalType<StringType> subject; // sub
|
||||
OptionalType<VectorType<StringType>> audience; // aud
|
||||
OptionalType<uint64_t> issuedAtUnixTime; // iat
|
||||
OptionalType<uint64_t> expiresAtUnixTime; // exp
|
||||
OptionalType<uint64_t> notBeforeUnixTime; // nbf
|
||||
OptionalType<StringType> tokenId; // jti
|
||||
OptionalType<VectorType<StringType>> tenants; // tenants
|
||||
// signature part
|
||||
StringType signature;
|
||||
};
|
||||
|
||||
} // namespace authz::jwt
|
||||
|
||||
#endif /*FDBRPC_TOKEN_SPEC_H*/
|
|
@ -45,6 +45,7 @@ void forceLinkCompressionUtilsTest();
|
|||
void forceLinkAtomicTests();
|
||||
void forceLinkIdempotencyIdTests();
|
||||
void forceLinkBlobConnectionProviderTests();
|
||||
void forceLinkArenaStringTests();
|
||||
|
||||
struct UnitTestWorkload : TestWorkload {
|
||||
static constexpr auto NAME = "UnitTests";
|
||||
|
@ -106,6 +107,7 @@ struct UnitTestWorkload : TestWorkload {
|
|||
forceLinkAtomicTests();
|
||||
forceLinkIdempotencyIdTests();
|
||||
forceLinkBlobConnectionProviderTests();
|
||||
forceLinkArenaStringTests();
|
||||
}
|
||||
|
||||
Future<Void> setup(Database const& cx) override {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
#include "flow/UnitTest.h"
|
||||
#include "flow/ArenaAllocator.h"
|
||||
#include "flow/ArenaString.h"
|
||||
|
||||
TEST_CASE("/flow/ArenaString") {
|
||||
Arena arena;
|
||||
ArenaAllocator<char> alloc(arena);
|
||||
{
|
||||
ArenaString s("1", alloc);
|
||||
auto shortStrBuf = s.data();
|
||||
s.assign(100, '1');
|
||||
auto longStrBuf = s.data();
|
||||
ASSERT_NE(shortStrBuf, longStrBuf);
|
||||
ArenaString t = s;
|
||||
auto copiedStrBuf = t.data();
|
||||
ASSERT_NE(copiedStrBuf, longStrBuf);
|
||||
}
|
||||
{
|
||||
ArenaString s(alloc);
|
||||
s.assign(100, 'a');
|
||||
ArenaString t(100, 'a', alloc);
|
||||
ASSERT(s == t);
|
||||
}
|
||||
{
|
||||
// Default construction of string does not specify an allocator, and Arena by extension.
|
||||
// Any modification that requires allocation will throw bad_allocator() when assigning beyond
|
||||
// short-string-optimized length.
|
||||
ArenaString s;
|
||||
bool hit = false;
|
||||
try {
|
||||
s.assign(100, 'a');
|
||||
} catch (Error& e) {
|
||||
hit = true;
|
||||
ASSERT_EQ(e.code(), error_code_bad_allocator);
|
||||
}
|
||||
ASSERT(hit);
|
||||
}
|
||||
{
|
||||
// string_view may be used to bridge strings with different allocators
|
||||
ArenaString s(100, 'a', alloc);
|
||||
std::string_view sv(s);
|
||||
std::string s2(sv);
|
||||
std::string_view sv2(s2);
|
||||
ASSERT(sv == sv2);
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
void forceLinkArenaStringTests() {}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* ArenaAllocator.h
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* 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 FLOW_ARENA_ALLOCATOR_H
|
||||
#define FLOW_ARENA_ALLOCATOR_H
|
||||
#pragma once
|
||||
|
||||
#include "flow/Arena.h"
|
||||
#include "flow/Error.h"
|
||||
#include "flow/FastRef.h"
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
template <class T>
|
||||
class ArenaAllocator {
|
||||
Arena* arenaPtr;
|
||||
|
||||
Arena& arena() noexcept { return *arenaPtr; }
|
||||
|
||||
public:
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using void_pointer = void*;
|
||||
using const_void_pointer = const void*;
|
||||
using self_type = ArenaAllocator<T>;
|
||||
using size_type = size_t;
|
||||
using value_type = T;
|
||||
using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
||||
|
||||
// Unfortunately this needs to exist due to STL's internal use of Allocator() in internal coding
|
||||
ArenaAllocator() noexcept : arenaPtr(nullptr) {}
|
||||
|
||||
ArenaAllocator(Arena& arena) noexcept : arenaPtr(&arena) {}
|
||||
|
||||
ArenaAllocator(const self_type& other) noexcept = default;
|
||||
|
||||
// Rebind constructor does not modify
|
||||
template <class U>
|
||||
ArenaAllocator(const ArenaAllocator<U>& other) noexcept : arenaPtr(other.arenaPtr) {}
|
||||
|
||||
ArenaAllocator& operator=(const self_type& other) noexcept = default;
|
||||
|
||||
ArenaAllocator(self_type&& other) noexcept = default;
|
||||
|
||||
ArenaAllocator& operator=(self_type&& other) noexcept = default;
|
||||
|
||||
T* allocate(size_t n) {
|
||||
if (!arenaPtr)
|
||||
throw bad_allocator();
|
||||
return new (arena()) T[n];
|
||||
}
|
||||
|
||||
void deallocate(T*, size_t) noexcept {}
|
||||
|
||||
bool operator==(const self_type& other) const noexcept { return arenaPtr == other.arenaPtr; }
|
||||
|
||||
bool operator!=(const self_type& other) const noexcept { return !(*this == other); }
|
||||
|
||||
template <class U>
|
||||
struct rebind {
|
||||
using other = ArenaAllocator<U>;
|
||||
};
|
||||
|
||||
using is_always_equal = std::false_type;
|
||||
using propagate_on_container_copy_assignment = std::true_type;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using propagate_on_container_swap = std::true_type;
|
||||
};
|
||||
|
||||
#endif /*FLOW_ARENA_ALLOCATOR_H*/
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* ArenaString.h
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* 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 FLOW_ARENA_STRING_H
|
||||
#define FLOW_ARENA_STRING_H
|
||||
#pragma once
|
||||
|
||||
#include "flow/ArenaAllocator.h"
|
||||
#include "flow/CustomAllocatorString.h"
|
||||
|
||||
using ArenaString = CustomAllocatorString<ArenaAllocator>;
|
||||
|
||||
#endif /*FLOW_ARENA_STRING_H*/
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* CustomAllocatorString.h
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* 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 FLOW_CUSTOM_ALLOCATOR_STRING_H
|
||||
#define FLOW_CUSTOM_ALLOCATOR_STRING_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
template <template <class> class Allocator>
|
||||
using CustomAllocatorString = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
|
||||
|
||||
#endif /*FLOW_CUSTOM_ALLOCATOR_STRING_H*/
|
|
@ -140,6 +140,7 @@ ERROR( storage_quota_exceeded, 1225, "Exceeded the maximum storage quota allocat
|
|||
ERROR( platform_error, 1500, "Platform error" )
|
||||
ERROR( large_alloc_failed, 1501, "Large block allocation failed" )
|
||||
ERROR( performance_counter_error, 1502, "QueryPerformanceCounter error" )
|
||||
ERROR( bad_allocator, 1503, "Null allocator was used to allocate memory" )
|
||||
|
||||
ERROR( io_error, 1510, "Disk i/o operation failed" )
|
||||
ERROR( file_not_found, 1511, "File not found" )
|
||||
|
|
Loading…
Reference in New Issue