265 lines
8.9 KiB
C++
265 lines
8.9 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and 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 <cstdint>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
#include <folly/CPortability.h>
|
|
#include <folly/Portability.h>
|
|
#include <folly/detail/base64_detail/Base64Api.h>
|
|
#include <folly/detail/base64_detail/Base64Common.h>
|
|
#include <folly/lang/Exception.h>
|
|
#include <folly/memory/UninitializedMemoryHacks.h>
|
|
|
|
namespace folly {
|
|
|
|
//
|
|
// base64 encoding/decoding
|
|
//
|
|
// There are a few variations of base64 encoding.
|
|
//
|
|
// We have 2: base64 and base64URL.
|
|
//
|
|
// base64 uses '+' '/' for encoding 62 and 63 and uses '=' padding symbol.
|
|
// (padding symbols are required on decoding)
|
|
//
|
|
// base64URL uses '-' '_' for encoding 62 and 63 and has no padding.
|
|
// Decoding with base64URL will accept both base64 and base64URL encoded data +
|
|
// padding is always optional.
|
|
//
|
|
// SIMD implementation is based on 0x80 blog.
|
|
// See details explained in folly/detail/base64_detail/README.md
|
|
//
|
|
|
|
//
|
|
// High level API.
|
|
// Encoding never fails, except for allocation.
|
|
// Decoding will throw base64_decode_error if it fails.
|
|
//
|
|
// NOTE: the expection does not contain detailed information
|
|
// about the error because keeping track of that is overhead.
|
|
// We can potentially improve error reporting by doing a second
|
|
// pass if we decide that it's benefitial.
|
|
|
|
struct base64_decode_error;
|
|
|
|
inline auto base64Encode(std::string_view s) -> std::string;
|
|
inline auto base64Decode(std::string_view s) -> std::string;
|
|
inline auto base64URLEncode(std::string_view s) -> std::string;
|
|
inline auto base64URLDecode(std::string_view s) -> std::string;
|
|
|
|
// Low level API.
|
|
//
|
|
// This API does not throw and is constexpr enabled.
|
|
//
|
|
// Encode returns a pointer past the last the byte written
|
|
// Decode returns a struct with `is_success` flag and the pointer `o`
|
|
// past the last char written.
|
|
//
|
|
// NOTE: decode will not stop writing when encountering a failure
|
|
// and can always write up to size.
|
|
//
|
|
// NOTE: since on C++17 we cannot always adequately determine if
|
|
// the function is running in compile time or not,
|
|
// we provide explicit runime versions too.
|
|
|
|
constexpr std::size_t base64EncodedSize(std::size_t inSize) noexcept;
|
|
constexpr std::size_t base64URLEncodedSize(std::size_t inSize) noexcept;
|
|
|
|
inline constexpr char* base64Encode(
|
|
const char* f, const char* l, char* o) noexcept;
|
|
inline constexpr char* base64URLEncode(
|
|
const char* f, const char* l, char* o) noexcept;
|
|
|
|
inline char* base64EncodeRuntime(
|
|
const char* f, const char* l, char* o) noexcept;
|
|
inline char* base64URLEncodeRuntime(
|
|
const char* f, const char* l, char* o) noexcept;
|
|
|
|
constexpr std::size_t base64DecodedSize(const char* f, const char* l) noexcept;
|
|
constexpr std::size_t base64DecodedSize(std::string_view s) noexcept;
|
|
|
|
constexpr std::size_t base64URLDecodedSize(
|
|
const char* f, const char* l) noexcept;
|
|
constexpr std::size_t base64URLDecodedSize(std::string_view s) noexcept;
|
|
|
|
struct base64_decode_result {
|
|
bool is_success;
|
|
char* o;
|
|
};
|
|
|
|
inline constexpr base64_decode_result base64Decode(
|
|
const char* f, const char* l, char* o) noexcept;
|
|
inline constexpr base64_decode_result base64Decode(
|
|
std::string_view s, char* o) noexcept;
|
|
|
|
inline constexpr base64_decode_result base64URLDecode(
|
|
const char* f, const char* l, char* o) noexcept;
|
|
inline constexpr base64_decode_result base64URLDecode(
|
|
std::string_view s, char* o) noexcept;
|
|
|
|
inline base64_decode_result base64DecodeRuntime(
|
|
const char* f, const char* l, char* o) noexcept;
|
|
inline base64_decode_result base64DecodeRuntime(
|
|
std::string_view s, char* o) noexcept;
|
|
|
|
inline base64_decode_result base64URLDecodeRuntime(
|
|
const char* f, const char* l, char* o) noexcept;
|
|
inline base64_decode_result base64URLDecodeRuntime(
|
|
std::string_view s, char* o) noexcept;
|
|
|
|
// -----------------------------------------------------------------
|
|
// implementation
|
|
|
|
struct base64_decode_error : std::runtime_error {
|
|
using std::runtime_error::runtime_error;
|
|
};
|
|
|
|
constexpr std::size_t base64EncodedSize(std::size_t inSize) noexcept {
|
|
return detail::base64_detail::base64EncodedSize(inSize);
|
|
}
|
|
|
|
constexpr std::size_t base64URLEncodedSize(std::size_t inSize) noexcept {
|
|
return detail::base64_detail::base64URLEncodedSize(inSize);
|
|
}
|
|
|
|
inline constexpr char* base64Encode(
|
|
const char* f, const char* l, char* o) noexcept {
|
|
return detail::base64_detail::base64Encode(f, l, o);
|
|
}
|
|
|
|
inline constexpr char* base64URLEncode(
|
|
const char* f, const char* l, char* o) noexcept {
|
|
return detail::base64_detail::base64URLEncode(f, l, o);
|
|
}
|
|
|
|
inline char* base64EncodeRuntime(
|
|
const char* f, const char* l, char* o) noexcept {
|
|
return detail::base64_detail::base64EncodeRuntime(f, l, o);
|
|
}
|
|
|
|
inline char* base64URLEncodeRuntime(
|
|
const char* f, const char* l, char* o) noexcept {
|
|
return detail::base64_detail::base64URLEncodeRuntime(f, l, o);
|
|
}
|
|
|
|
inline std::string base64Encode(std::string_view s) {
|
|
std::string res;
|
|
std::size_t resSize = folly::base64EncodedSize(s.size());
|
|
folly::resizeWithoutInitialization(res, resSize);
|
|
folly::base64EncodeRuntime(s.data(), s.data() + s.size(), res.data());
|
|
return res;
|
|
}
|
|
|
|
inline std::string base64URLEncode(std::string_view s) {
|
|
std::string res;
|
|
std::size_t resSize = folly::base64URLEncodedSize(s.size());
|
|
folly::resizeWithoutInitialization(res, resSize);
|
|
folly::base64URLEncodeRuntime(s.data(), s.data() + s.size(), res.data());
|
|
return res;
|
|
}
|
|
|
|
constexpr std::size_t base64DecodedSize(const char* f, const char* l) noexcept {
|
|
return detail::base64_detail::base64DecodedSize(f, l);
|
|
}
|
|
|
|
constexpr std::size_t base64DecodedSize(std::string_view s) noexcept {
|
|
return folly::base64DecodedSize(s.data(), s.data() + s.size());
|
|
}
|
|
|
|
constexpr std::size_t base64URLDecodedSize(
|
|
const char* f, const char* l) noexcept {
|
|
return detail::base64_detail::base64URLDecodedSize(f, l);
|
|
}
|
|
|
|
constexpr std::size_t base64URLDecodedSize(std::string_view s) noexcept {
|
|
return folly::base64URLDecodedSize(s.data(), s.data() + s.size());
|
|
}
|
|
|
|
inline constexpr base64_decode_result base64Decode(
|
|
const char* f, const char* l, char* o) noexcept {
|
|
auto detailResult = detail::base64_detail::base64Decode(f, l, o);
|
|
return {detailResult.isSuccess, detailResult.o};
|
|
}
|
|
|
|
inline constexpr base64_decode_result base64Decode(
|
|
std::string_view s, char* o) noexcept {
|
|
return folly::base64Decode(s.data(), s.data() + s.size(), o);
|
|
}
|
|
|
|
inline constexpr base64_decode_result base64URLDecode(
|
|
const char* f, const char* l, char* o) noexcept {
|
|
auto detailResult = detail::base64_detail::base64URLDecode(f, l, o);
|
|
return {detailResult.isSuccess, detailResult.o};
|
|
}
|
|
|
|
inline constexpr base64_decode_result base64URLDecode(
|
|
std::string_view s, char* o) noexcept {
|
|
return folly::base64URLDecode(s.data(), s.data() + s.size(), o);
|
|
}
|
|
|
|
inline base64_decode_result base64DecodeRuntime(
|
|
const char* f, const char* l, char* o) noexcept {
|
|
auto detailResult = detail::base64_detail::base64DecodeRuntime(f, l, o);
|
|
return {detailResult.isSuccess, detailResult.o};
|
|
}
|
|
|
|
inline base64_decode_result base64DecodeRuntime(
|
|
std::string_view s, char* o) noexcept {
|
|
return folly::base64DecodeRuntime(s.data(), s.data() + s.size(), o);
|
|
}
|
|
|
|
inline base64_decode_result base64URLDecodeRuntime(
|
|
const char* f, const char* l, char* o) noexcept {
|
|
auto detailResult = detail::base64_detail::base64URLDecodeRuntime(f, l, o);
|
|
return {detailResult.isSuccess, detailResult.o};
|
|
}
|
|
|
|
inline base64_decode_result base64URLDecodeRuntime(
|
|
std::string_view s, char* o) noexcept {
|
|
return folly::base64URLDecodeRuntime(s.data(), s.data() + s.size(), o);
|
|
}
|
|
|
|
// NOTE: for resizeWithoutInitialization we don't need to declare the macros,
|
|
// since we are using char which is already included by default.
|
|
inline std::string base64Decode(std::string_view s) {
|
|
std::string res;
|
|
std::size_t resSize = folly::base64DecodedSize(s);
|
|
folly::resizeWithoutInitialization(res, resSize);
|
|
|
|
if (!folly::base64DecodeRuntime(s, res.data()).is_success) {
|
|
folly::throw_exception<base64_decode_error>("Base64 Decoding failed");
|
|
}
|
|
return res;
|
|
}
|
|
|
|
inline std::string base64URLDecode(std::string_view s) {
|
|
std::string res;
|
|
std::size_t resSize = folly::base64URLDecodedSize(s);
|
|
folly::resizeWithoutInitialization(res, resSize);
|
|
|
|
if (!folly::base64URLDecodeRuntime(s, res.data()).is_success) {
|
|
folly::throw_exception<base64_decode_error>("Base64URL Decoding failed");
|
|
}
|
|
return res;
|
|
}
|
|
|
|
} // namespace folly
|