diff --git a/cpp/src/parquet/encryption/external/dbpa_interface.h b/cpp/src/parquet/encryption/external/dbpa_interface.h index 4478ebd9cbd1..5889f5c70363 100644 --- a/cpp/src/parquet/encryption/external/dbpa_interface.h +++ b/cpp/src/parquet/encryption/external/dbpa_interface.h @@ -2,31 +2,109 @@ #pragma once +#include +#include +#include #include -#include "parquet/platform.h" -#include "arrow/util/span.h" +#include +#include +#include "span.hpp" +#include "enums.h" -using ::arrow::util::span; +#ifndef DBPS_EXPORT +#define DBPS_EXPORT +#endif -namespace parquet::encryption::external { +// TODO: this file was copied from +// https://github.com/protegrity/DataBatchProtectionService +// we need to find a better way to share it between repos. - //TODO: this will change once we have a solid defition of interfaces +namespace dbps::external { - class EncryptionResult { - }; +using tcb::span; - class DecryptionResult { - }; +/* + * DataBatchProtectionAgentInterface, EncryptionResult and DecryptionResult implementation contracts: + * - While handle to EncryptionResult/DecryptionResult exists, ciphertext()/plaintext() is guaranteed to return valid data + * - Read operations are not destructive. Multiple calls return the same data + * - Destructor must dispose of internal memory (either by delegation or cleanup) + * - No throwing exceptions. Errors reported via success() flag and error methods. + * - Library users must check size() to ensure the actual size of the returned payload. + */ - class PARQUET_EXPORT DataBatchProtectionAgentInterface { - public: - virtual std::unique_ptr Encrypt( - span plaintext, - span ciphertext) = 0; +class DBPS_EXPORT EncryptionResult { +public: + virtual span ciphertext() const = 0; - virtual std::unique_ptr Decrypt( - span ciphertext) = 0; + // Allows a larger backing buffer than the exact ciphertext size. + // Library users must check size() to ensure the actual size of the returned payload. + virtual std::size_t size() const = 0; - virtual ~DataBatchProtectionAgentInterface() = default; - }; -} + // Success flag; false indicates an error. + virtual bool success() const = 0; + + // Error details (valid when success() == false). + virtual const std::string& error_message() const = 0; + virtual const std::map& error_fields() const = 0; + + virtual ~EncryptionResult() = default; +}; + +class DBPS_EXPORT DecryptionResult { +public: + virtual span plaintext() const = 0; + + // Allows a larger backing buffer than the exact plaintext size. + // Library users must check size() to ensure the actual size of the returned payload. + virtual std::size_t size() const = 0; + + // Success flag; false indicates an error. + virtual bool success() const = 0; + + // Error details (valid when success() == false). + virtual const std::string& error_message() const = 0; + virtual const std::map& error_fields() const = 0; + + virtual ~DecryptionResult() = default; +}; + +class DBPS_EXPORT DataBatchProtectionAgentInterface { +public: + DataBatchProtectionAgentInterface() = default; + + // user_id is not stored as a member; it is expected to be embedded into app_context + // (e.g., as a serialized map/JSON field). + virtual void init( + std::string column_name, + std::map connection_config, + std::string app_context, + std::string column_key_id, + Type::type data_type, + CompressionCodec::type compression_type) + { + column_name_ = std::move(column_name); + connection_config_ = std::move(connection_config); + app_context_ = std::move(app_context); + column_key_id_ = std::move(column_key_id); + data_type_ = data_type; + compression_type_ = compression_type; + } + + virtual std::unique_ptr Encrypt( + span plaintext) = 0; + + virtual std::unique_ptr Decrypt( + span ciphertext) = 0; + + virtual ~DataBatchProtectionAgentInterface() = default; + +private: + std::string column_name_; + std::map connection_config_; + std::string app_context_; // includes user_id + + std::string column_key_id_; + Type::type data_type_; + CompressionCodec::type compression_type_; +}; +} \ No newline at end of file diff --git a/cpp/src/parquet/encryption/external/dbpa_library_wrapper.cc b/cpp/src/parquet/encryption/external/dbpa_library_wrapper.cc index d62c04eb055a..a0fd7e7f13e9 100644 --- a/cpp/src/parquet/encryption/external/dbpa_library_wrapper.cc +++ b/cpp/src/parquet/encryption/external/dbpa_library_wrapper.cc @@ -2,8 +2,7 @@ #include "parquet/encryption/external/dbpa_library_wrapper.h" #include "parquet/encryption/external/dbpa_interface.h" -#include "arrow/util/span.h" -#include + #include #include @@ -11,8 +10,6 @@ #include "arrow/util/io_util.h" -using ::arrow::util::span; - namespace parquet::encryption::external { // Default implementation for handle closing function @@ -60,21 +57,5 @@ DBPALibraryWrapper::~DBPALibraryWrapper() { // Now we can close the shared library using the provided function handle_closing_fn_(library_handle_); library_handle_ = nullptr; -} - -// Decorator implementation of Encrypt method -std::unique_ptr DBPALibraryWrapper::Encrypt( - span plaintext, - span ciphertext) { - - return wrapped_agent_->Encrypt(plaintext, ciphertext); -} - -// Decorator implementation of Decrypt method -std::unique_ptr DBPALibraryWrapper::Decrypt( - span ciphertext) { - - return wrapped_agent_->Decrypt(ciphertext); -} - -} // namespace parquet::encryption::external +} //DBPALibraryWrapper::~DBPALibraryWrapper() +} // namespace parquet::encryption::external diff --git a/cpp/src/parquet/encryption/external/dbpa_library_wrapper.h b/cpp/src/parquet/encryption/external/dbpa_library_wrapper.h index 7eae970e19da..285a12fe2011 100644 --- a/cpp/src/parquet/encryption/external/dbpa_library_wrapper.h +++ b/cpp/src/parquet/encryption/external/dbpa_library_wrapper.h @@ -6,12 +6,18 @@ #include #include "parquet/encryption/external/dbpa_interface.h" -#include "arrow/util/span.h" +#include "span.hpp" -using ::arrow::util::span; +using tcb::span; namespace parquet::encryption::external { +using dbps::external::DataBatchProtectionAgentInterface; +using dbps::external::EncryptionResult; +using dbps::external::DecryptionResult; +using dbps::external::Type; +using dbps::external::CompressionCodec; + // Default implementation for shared library closing function // This is passed into the constructor of DBPALibraryWrapper, // and is used as the default function to close the shared library. @@ -19,7 +25,7 @@ void DefaultSharedLibraryClosingFn(void* library_handle); // Decorator/Wrapper class for the DataBatchProtectionAgentInterface // Its main purpose is to close the shared library when Arrow is about to destroy -// an intance of an DPBAgent +// an instance of an DBPAgent // // In the constructor we allow to pass a function that will be used to close the shared library. // This simplifies testing, as we can use a mock function to avoid actually closing the shared library. @@ -44,16 +50,32 @@ class DBPALibraryWrapper : public DataBatchProtectionAgentInterface { // Doing it in a different order, may cause issues, as by unloading the library may cause the class // definition to be unloaded before the destructor completes, and that is likely to cause issues // (such as a segfault). - ~DBPALibraryWrapper() override; + ~DBPALibraryWrapper(); + + // Decorator implementation of init method + inline void init( + std::string column_name, + std::map connection_config, + std::string app_context, + std::string column_key_id, + Type::type data_type, + CompressionCodec::type compression_type) override { + wrapped_agent_->init(std::move(column_name), std::move(connection_config), + std::move(app_context), std::move(column_key_id), + data_type, compression_type); + } - // Decorator implementation of Encrypt method - std::unique_ptr Encrypt( - span plaintext, - span ciphertext) override; + // Decorator implementation of Encrypt method - inlined for performance + inline std::unique_ptr Encrypt( + span plaintext) override { + return wrapped_agent_->Encrypt(plaintext); + } - // Decorator implementation of Decrypt method - std::unique_ptr Decrypt( - span ciphertext) override; + // Decorator implementation of Decrypt method - inlined for performance + inline std::unique_ptr Decrypt( + span ciphertext) override { + return wrapped_agent_->Decrypt(ciphertext); + } }; -} // namespace parquet::encryption::external +} // namespace parquet::encryption::external diff --git a/cpp/src/parquet/encryption/external/dbpa_library_wrapper_test.cc b/cpp/src/parquet/encryption/external/dbpa_library_wrapper_test.cc index 72cf34bf0f7a..fc2f2fa2b148 100644 --- a/cpp/src/parquet/encryption/external/dbpa_library_wrapper_test.cc +++ b/cpp/src/parquet/encryption/external/dbpa_library_wrapper_test.cc @@ -20,17 +20,74 @@ #include #include #include +#include #include "gtest/gtest.h" #include "parquet/encryption/external/dbpa_interface.h" #include "parquet/encryption/external/dbpa_library_wrapper.h" -#include "arrow/util/span.h" + #include "parquet/test_util.h" +#include "span.hpp" -using ::arrow::util::span; +using tcb::span; namespace parquet::encryption::external::test { +using dbps::external::EncryptionResult; +using dbps::external::DecryptionResult; +using dbps::external::Type; +using dbps::external::CompressionCodec; + +// Simple implementation of EncryptionResult for testing +class TestEncryptionResult : public EncryptionResult { +public: + TestEncryptionResult(std::vector data, bool success = true, + std::string error_msg = "", + std::map error_fields = {}) + : ciphertext_data_(std::move(data)), success_(success), + error_message_(std::move(error_msg)), error_fields_(std::move(error_fields)) {} + + span ciphertext() const override { + return span(ciphertext_data_.data(), ciphertext_data_.size()); + } + + std::size_t size() const override { return ciphertext_data_.size(); } + bool success() const override { return success_; } + const std::string& error_message() const override { return error_message_; } + const std::map& error_fields() const override { return error_fields_; } + +private: + std::vector ciphertext_data_; + bool success_; + std::string error_message_; + std::map error_fields_; +}; + +// Simple implementation of DecryptionResult for testing +class TestDecryptionResult : public DecryptionResult { +public: + TestDecryptionResult(std::vector data, bool success = true, + std::string error_msg = "", + std::map error_fields = {}) + : plaintext_data_(std::move(data)), success_(success), + error_message_(std::move(error_msg)), error_fields_(std::move(error_fields)) {} + + span plaintext() const override { + return span(plaintext_data_.data(), plaintext_data_.size()); + } + + std::size_t size() const override { return plaintext_data_.size(); } + bool success() const override { return success_; } + const std::string& error_message() const override { return error_message_; } + const std::map& error_fields() const override { return error_fields_; } + +private: + std::vector plaintext_data_; + bool success_; + std::string error_message_; + std::map error_fields_; +}; + // Companion object to track the order of destruction events class DestructionOrderTracker { public: @@ -85,30 +142,43 @@ class DestructionOrderTracker { }; //DestructionOrderTracker // Companion object to hold mock state that persists after mock instance destruction -class MockCompanionDPBA { +class MockCompanionDBPA { public: - MockCompanionDPBA(std::shared_ptr order_tracker = nullptr) + MockCompanionDBPA(std::shared_ptr order_tracker = nullptr) : encrypt_called_(false), decrypt_called_(false), + init_called_(false), destructor_called_(false), encrypt_count_(0), decrypt_count_(0), + init_count_(0), order_tracker_(order_tracker ? order_tracker : std::make_shared()) {} // Test helper methods bool WasEncryptCalled() const { return encrypt_called_; } bool WasDecryptCalled() const { return decrypt_called_; } + bool WasInitCalled() const { return init_called_; } bool WasDestructorCalled() const { return destructor_called_; } int GetEncryptCount() const { return encrypt_count_; } int GetDecryptCount() const { return decrypt_count_; } + int GetInitCount() const { return init_count_; } const std::vector& GetEncryptPlaintext() const { return encrypt_plaintext_; } const std::vector& GetDecryptCiphertext() const { return decrypt_ciphertext_; } size_t GetEncryptCiphertextSize() const { return encrypt_ciphertext_size_; } std::shared_ptr GetOrderTracker() const { return order_tracker_; } + // Init tracking methods + const std::string& GetInitColumnName() const { return init_column_name_; } + const std::map& GetInitConnectionConfig() const { return init_connection_config_; } + const std::string& GetInitAppContext() const { return init_app_context_; } + const std::string& GetInitColumnKeyId() const { return init_column_key_id_; } + Type::type GetInitDataType() const { return init_data_type_; } + CompressionCodec::type GetInitCompressionType() const { return init_compression_type_; } + // State update methods (called by the mock instance) void SetEncryptCalled(bool called) { encrypt_called_ = called; } void SetDecryptCalled(bool called) { decrypt_called_ = called; } + void SetInitCalled(bool called) { init_called_ = called; } void SetDestructorCalled(bool called) { destructor_called_ = called; if (called) { @@ -117,21 +187,48 @@ class MockCompanionDPBA { } void IncrementEncryptCount() { encrypt_count_++; } void IncrementDecryptCount() { decrypt_count_++; } + void IncrementInitCount() { init_count_++; } void SetEncryptPlaintext(const std::vector& plaintext) { encrypt_plaintext_ = plaintext; } void SetDecryptCiphertext(const std::vector& ciphertext) { decrypt_ciphertext_ = ciphertext; } void SetEncryptCiphertextSize(size_t size) { encrypt_ciphertext_size_ = size; } + + // Init parameter tracking + void SetInitParameters( + std::string column_name, + std::map connection_config, + std::string app_context, + std::string column_key_id, + Type::type data_type, + CompressionCodec::type compression_type) { + init_column_name_ = std::move(column_name); + init_connection_config_ = std::move(connection_config); + init_app_context_ = std::move(app_context); + init_column_key_id_ = std::move(column_key_id); + init_data_type_ = data_type; + init_compression_type_ = compression_type; + } private: bool encrypt_called_; bool decrypt_called_; + bool init_called_; bool destructor_called_; int encrypt_count_; int decrypt_count_; + int init_count_; std::vector encrypt_plaintext_; std::vector decrypt_ciphertext_; size_t encrypt_ciphertext_size_; std::shared_ptr order_tracker_; -}; //MockCompanionDPBA + + // Init parameters + std::string init_column_name_; + std::map init_connection_config_; + std::string init_app_context_; + std::string init_column_key_id_; + Type::type init_data_type_; + CompressionCodec::type init_compression_type_; +}; //MockCompanionDBPA // Companion object to track shared library handle management operations class SharedLibHandleManagementCompanion { @@ -174,27 +271,41 @@ class SharedLibHandleManagementCompanion { // Mock implementation of DataBatchProtectionAgentInterface for testing delegation class MockDataBatchProtectionAgent : public DataBatchProtectionAgentInterface { public: - explicit MockDataBatchProtectionAgent(std::shared_ptr companion = nullptr) - : companion_(companion ? companion : std::make_shared()) {} + explicit MockDataBatchProtectionAgent(std::shared_ptr companion = nullptr) + : companion_(companion ? companion : std::make_shared()) {} ~MockDataBatchProtectionAgent() override { companion_->SetDestructorCalled(true); } + void init( + std::string column_name, + std::map connection_config, + std::string app_context, + std::string column_key_id, + Type::type data_type, + CompressionCodec::type compression_type) override { + companion_->SetInitCalled(true); + companion_->IncrementInitCount(); + companion_->SetInitParameters( + std::move(column_name), + std::move(connection_config), + std::move(app_context), + std::move(column_key_id), + data_type, + compression_type); + } + std::unique_ptr Encrypt( - span plaintext, - span ciphertext) override { + span plaintext) override { companion_->SetEncryptCalled(true); companion_->IncrementEncryptCount(); companion_->SetEncryptPlaintext(std::vector(plaintext.begin(), plaintext.end())); - companion_->SetEncryptCiphertextSize(ciphertext.size()); - - // Simple mock encryption: just copy plaintext to ciphertext - if (ciphertext.size() >= plaintext.size()) { - std::copy(plaintext.begin(), plaintext.end(), ciphertext.begin()); - } + companion_->SetEncryptCiphertextSize(plaintext.size()); - return std::make_unique(); + // Create a simple mock encryption result + std::vector ciphertext_data(plaintext.begin(), plaintext.end()); + return std::make_unique(std::move(ciphertext_data)); } std::unique_ptr Decrypt( @@ -203,14 +314,16 @@ class MockDataBatchProtectionAgent : public DataBatchProtectionAgentInterface { companion_->IncrementDecryptCount(); companion_->SetDecryptCiphertext(std::vector(ciphertext.begin(), ciphertext.end())); - return std::make_unique(); + // Create a simple mock decryption result + std::vector plaintext_data(ciphertext.begin(), ciphertext.end()); + return std::make_unique(std::move(plaintext_data)); } // Getter for the companion object - std::shared_ptr GetCompanion() const { return companion_; } + std::shared_ptr GetCompanion() const { return companion_; } private: - std::shared_ptr companion_; + std::shared_ptr companion_; }; // Test fixture for DBPALibraryWrapper tests @@ -225,7 +338,7 @@ class DBPALibraryWrapperTest : public ::testing::Test { destruction_order_tracker_ = std::make_shared(); // Create companion objects with shared order tracker - mock_companion_ = std::make_shared(destruction_order_tracker_); + mock_companion_ = std::make_shared(destruction_order_tracker_); handle_companion_ = std::make_shared(destruction_order_tracker_); // Create mock agent @@ -265,7 +378,7 @@ class DBPALibraryWrapperTest : public ::testing::Test { std::string test_plaintext_; std::vector test_ciphertext_; std::shared_ptr destruction_order_tracker_; - std::shared_ptr mock_companion_; + std::shared_ptr mock_companion_; std::shared_ptr handle_companion_; std::unique_ptr mock_agent_; MockDataBatchProtectionAgent* mock_agent_ptr_; @@ -364,6 +477,64 @@ TEST_F(DBPALibraryWrapperTest, CustomHandleClosingFunction) { // DELEGATION FUNCTIONALITY TESTS // ============================================================================ +TEST_F(DBPALibraryWrapperTest, InitDelegation) { + auto wrapper = CreateWrapper(); + + // Test data for init parameters + std::string column_name = "test_column"; + std::map connection_config = { + {"host", "localhost"}, + {"port", "5432"}, + {"database", "testdb"} + }; + std::string app_context = "test_app_context"; + std::string column_key_id = "test_key_id"; + Type::type data_type = Type::INT32; + CompressionCodec::type compression_type = CompressionCodec::SNAPPY; + + // Call init through wrapper + wrapper->init(column_name, connection_config, app_context, column_key_id, data_type, compression_type); + + // Verify the mock agent was called + EXPECT_TRUE(mock_companion_->WasInitCalled()); + EXPECT_EQ(mock_companion_->GetInitCount(), 1); + + // Verify the correct parameters were passed to the mock + EXPECT_EQ(mock_companion_->GetInitColumnName(), column_name); + EXPECT_EQ(mock_companion_->GetInitConnectionConfig(), connection_config); + EXPECT_EQ(mock_companion_->GetInitAppContext(), app_context); + EXPECT_EQ(mock_companion_->GetInitColumnKeyId(), column_key_id); + EXPECT_EQ(mock_companion_->GetInitDataType(), data_type); + EXPECT_EQ(mock_companion_->GetInitCompressionType(), compression_type); +} + +TEST_F(DBPALibraryWrapperTest, InitDelegationWithEmptyParameters) { + auto wrapper = CreateWrapper(); + + // Test init with empty parameters + std::string empty_column_name = ""; + std::map empty_connection_config = {}; + std::string empty_app_context = ""; + std::string empty_column_key_id = ""; + Type::type data_type = Type::BYTE_ARRAY; + CompressionCodec::type compression_type = CompressionCodec::UNCOMPRESSED; + + // Call init through wrapper + wrapper->init(empty_column_name, empty_connection_config, empty_app_context, empty_column_key_id, data_type, compression_type); + + // Verify the mock agent was called + EXPECT_TRUE(mock_companion_->WasInitCalled()); + EXPECT_EQ(mock_companion_->GetInitCount(), 1); + + // Verify the empty parameters were passed correctly + EXPECT_EQ(mock_companion_->GetInitColumnName(), empty_column_name); + EXPECT_EQ(mock_companion_->GetInitConnectionConfig(), empty_connection_config); + EXPECT_EQ(mock_companion_->GetInitAppContext(), empty_app_context); + EXPECT_EQ(mock_companion_->GetInitColumnKeyId(), empty_column_key_id); + EXPECT_EQ(mock_companion_->GetInitDataType(), data_type); + EXPECT_EQ(mock_companion_->GetInitCompressionType(), compression_type); +} + TEST_F(DBPALibraryWrapperTest, EncryptDelegation) { auto wrapper = CreateWrapper(); @@ -374,7 +545,7 @@ TEST_F(DBPALibraryWrapperTest, EncryptDelegation) { span ciphertext_span(test_ciphertext_.data(), test_ciphertext_.size()); // Call encrypt through wrapper - auto result = wrapper->Encrypt(plaintext_span, ciphertext_span); + auto result = wrapper->Encrypt(plaintext_span); // Verify the mock agent was called EXPECT_TRUE(mock_companion_->WasEncryptCalled()); @@ -427,9 +598,8 @@ TEST_F(DBPALibraryWrapperTest, MultipleEncryptDelegations) { span plaintext_span( reinterpret_cast(plaintext.data()), plaintext.size()); - span ciphertext_span(ciphertext.data(), ciphertext.size()); - auto result = wrapper->Encrypt(plaintext_span, ciphertext_span); + auto result = wrapper->Encrypt(plaintext_span); EXPECT_NE(result, nullptr); } @@ -467,18 +637,16 @@ TEST_F(DBPALibraryWrapperTest, MixedOperationsDelegation) { for (const auto& data : test_data) { // Encrypt - std::vector ciphertext(data.size()); span plaintext_span( reinterpret_cast(data.data()), data.size()); - span ciphertext_span(ciphertext.data(), ciphertext.size()); - auto encrypt_result = wrapper->Encrypt(plaintext_span, ciphertext_span); + auto encrypt_result = wrapper->Encrypt(plaintext_span); EXPECT_NE(encrypt_result, nullptr); - // Decrypt - span decrypt_span(ciphertext.data(), ciphertext.size()); - auto decrypt_result = wrapper->Decrypt(decrypt_span); + // Decrypt using the ciphertext from the encryption result + auto ciphertext_span = encrypt_result->ciphertext(); + auto decrypt_result = wrapper->Decrypt(ciphertext_span); EXPECT_NE(decrypt_result, nullptr); } @@ -489,38 +657,81 @@ TEST_F(DBPALibraryWrapperTest, MixedOperationsDelegation) { EXPECT_EQ(mock_companion_->GetDecryptCount(), call_count); } +TEST_F(DBPALibraryWrapperTest, InitWithEncryptDecryptOperations) { + auto wrapper = CreateWrapper(); + + // First, initialize the wrapper + std::string column_name = "test_column"; + std::map connection_config = { + {"host", "localhost"}, + {"port", "5432"} + }; + std::string app_context = "test_app"; + std::string column_key_id = "test_key"; + Type::type data_type = Type::INT32; + CompressionCodec::type compression_type = CompressionCodec::SNAPPY; + + wrapper->init(column_name, connection_config, app_context, column_key_id, data_type, compression_type); + + // Verify init was called + EXPECT_TRUE(mock_companion_->WasInitCalled()); + EXPECT_EQ(mock_companion_->GetInitCount(), 1); + + // Then perform encrypt/decrypt operations + std::string test_data = "Test data after init"; + span plaintext_span( + reinterpret_cast(test_data.data()), + test_data.size()); + + auto encrypt_result = wrapper->Encrypt(plaintext_span); + EXPECT_NE(encrypt_result, nullptr); + + auto ciphertext_span = encrypt_result->ciphertext(); + auto decrypt_result = wrapper->Decrypt(ciphertext_span); + EXPECT_NE(decrypt_result, nullptr); + + // Verify all operations were called + EXPECT_TRUE(mock_companion_->WasEncryptCalled()); + EXPECT_TRUE(mock_companion_->WasDecryptCalled()); + EXPECT_EQ(mock_companion_->GetEncryptCount(), 1); + EXPECT_EQ(mock_companion_->GetDecryptCount(), 1); + + // Verify init parameters are still accessible + EXPECT_EQ(mock_companion_->GetInitColumnName(), column_name); + EXPECT_EQ(mock_companion_->GetInitConnectionConfig(), connection_config); + EXPECT_EQ(mock_companion_->GetInitAppContext(), app_context); + EXPECT_EQ(mock_companion_->GetInitColumnKeyId(), column_key_id); + EXPECT_EQ(mock_companion_->GetInitDataType(), data_type); + EXPECT_EQ(mock_companion_->GetInitCompressionType(), compression_type); +} + TEST_F(DBPALibraryWrapperTest, DelegationWithEmptyData) { auto wrapper = CreateWrapper(); // Test encryption with empty data std::vector empty_plaintext; - std::vector empty_ciphertext; span plaintext_span(empty_plaintext); - span ciphertext_span(empty_ciphertext); - auto encrypt_result = wrapper->Encrypt(plaintext_span, ciphertext_span); + auto encrypt_result = wrapper->Encrypt(plaintext_span); EXPECT_NE(encrypt_result, nullptr); EXPECT_TRUE(mock_companion_->WasEncryptCalled()); // Test decryption with empty data - span empty_ciphertext_span(empty_ciphertext); - auto decrypt_result = wrapper->Decrypt(empty_ciphertext_span); + auto ciphertext_span = encrypt_result->ciphertext(); + auto decrypt_result = wrapper->Decrypt(ciphertext_span); EXPECT_NE(decrypt_result, nullptr); EXPECT_TRUE(mock_companion_->WasDecryptCalled()); } -//TODO: we need to revisit this once we have a solid defition of interfaces -// and behavior whenever we have null data. TEST_F(DBPALibraryWrapperTest, DelegationWithNullData) { auto wrapper = CreateWrapper(); // Test encryption with null data pointers but valid spans // This tests that the wrapper properly delegates even with null data span null_plaintext_span(nullptr, size_t{0}); - span null_ciphertext_span(nullptr, size_t{0}); - auto encrypt_result = wrapper->Encrypt(null_plaintext_span, null_ciphertext_span); + auto encrypt_result = wrapper->Encrypt(null_plaintext_span); EXPECT_NE(encrypt_result, nullptr); EXPECT_TRUE(mock_companion_->WasEncryptCalled()); @@ -550,12 +761,10 @@ TEST_F(DBPALibraryWrapperTest, DestructorBasicBehavior) { // Perform some operations to ensure the wrapper is used std::vector plaintext = {1, 2, 3, 4, 5}; - std::vector ciphertext(plaintext.size()); span plaintext_span(plaintext.data(), plaintext.size()); - span ciphertext_span(ciphertext.data(), ciphertext.size()); - auto result = wrapper->Encrypt(plaintext_span, ciphertext_span); + auto result = wrapper->Encrypt(plaintext_span); EXPECT_NE(result, nullptr); // Verify handle closing hasn't been called yet @@ -584,13 +793,12 @@ TEST_F(DBPALibraryWrapperTest, DestructorWithMultipleOperations) { span plaintext_span( reinterpret_cast(plaintext.data()), plaintext.size()); - span ciphertext_span(ciphertext.data(), ciphertext.size()); - auto encrypt_result = wrapper->Encrypt(plaintext_span, ciphertext_span); + auto encrypt_result = wrapper->Encrypt(plaintext_span); EXPECT_NE(encrypt_result, nullptr); - span ciphertext_span_const(ciphertext.data(), ciphertext.size()); - auto decrypt_result = wrapper->Decrypt(ciphertext_span_const); + auto ciphertext_span = encrypt_result->ciphertext(); + auto decrypt_result = wrapper->Decrypt(ciphertext_span); EXPECT_NE(decrypt_result, nullptr); } @@ -609,7 +817,7 @@ TEST_F(DBPALibraryWrapperTest, DestructorOrderVerification) { destruction_order_tracker_->Clear(); // Create a custom mock agent that tracks destruction order - auto custom_companion = std::make_shared(destruction_order_tracker_); + auto custom_companion = std::make_shared(destruction_order_tracker_); auto custom_agent = std::make_unique(custom_companion); void* dummy_handle = reinterpret_cast(0x12345678); @@ -620,12 +828,10 @@ TEST_F(DBPALibraryWrapperTest, DestructorOrderVerification) { // Perform some operations std::vector plaintext = {1, 2, 3}; - std::vector ciphertext(plaintext.size()); span plaintext_span(plaintext.data(), plaintext.size()); - span ciphertext_span(ciphertext.data(), ciphertext.size()); - auto result = wrapper->Encrypt(plaintext_span, ciphertext_span); + auto result = wrapper->Encrypt(plaintext_span); EXPECT_NE(result, nullptr); // Verify neither destructor nor handle closing has been called yet @@ -689,18 +895,33 @@ TEST_F(DBPALibraryWrapperTest, InterfaceCompliancePolymorphic) { DataBatchProtectionAgentInterface* interface_ptr = wrapper.get(); EXPECT_NE(interface_ptr, nullptr); - // Test polymorphic calls + // Test polymorphic init call + std::string column_name = "polymorphic_column"; + std::map connection_config = {{"test", "value"}}; + std::string app_context = "polymorphic_context"; + std::string column_key_id = "polymorphic_key"; + Type::type data_type = Type::INT64; + CompressionCodec::type compression_type = CompressionCodec::GZIP; + + interface_ptr->init(column_name, connection_config, app_context, column_key_id, data_type, compression_type); + + // Verify init was called through the interface + EXPECT_TRUE(mock_companion_->WasInitCalled()); + EXPECT_EQ(mock_companion_->GetInitCount(), 1); + EXPECT_EQ(mock_companion_->GetInitColumnName(), column_name); + EXPECT_EQ(mock_companion_->GetInitDataType(), data_type); + EXPECT_EQ(mock_companion_->GetInitCompressionType(), compression_type); + + // Test polymorphic encrypt/decrypt calls std::vector plaintext = {1, 2, 3}; - std::vector ciphertext(plaintext.size()); span plaintext_span(plaintext.data(), plaintext.size()); - span ciphertext_span(ciphertext.data(), ciphertext.size()); - auto encrypt_result = interface_ptr->Encrypt(plaintext_span, ciphertext_span); + auto encrypt_result = interface_ptr->Encrypt(plaintext_span); EXPECT_NE(encrypt_result, nullptr); - span ciphertext_span_const(ciphertext.data(), ciphertext.size()); - auto decrypt_result = interface_ptr->Decrypt(ciphertext_span_const); + auto ciphertext_span = encrypt_result->ciphertext(); + auto decrypt_result = interface_ptr->Decrypt(ciphertext_span); EXPECT_NE(decrypt_result, nullptr); // Verify the mock agent was called through the interface @@ -719,13 +940,12 @@ TEST_F(DBPALibraryWrapperTest, EdgeCaseZeroSizeSpans) { std::vector empty_data; span empty_plaintext_span(empty_data); - span empty_ciphertext_span(empty_data); - auto encrypt_result = wrapper->Encrypt(empty_plaintext_span, empty_ciphertext_span); + auto encrypt_result = wrapper->Encrypt(empty_plaintext_span); EXPECT_NE(encrypt_result, nullptr); - span empty_decrypt_span(empty_data); - auto decrypt_result = wrapper->Decrypt(empty_decrypt_span); + auto ciphertext_span = encrypt_result->ciphertext(); + auto decrypt_result = wrapper->Decrypt(ciphertext_span); EXPECT_NE(decrypt_result, nullptr); EXPECT_TRUE(mock_companion_->WasEncryptCalled()); @@ -737,16 +957,14 @@ TEST_F(DBPALibraryWrapperTest, EdgeCaseSingleByteData) { // Test with single byte data std::vector single_byte = {0x42}; - std::vector single_byte_ciphertext(1); span plaintext_span(single_byte.data(), single_byte.size()); - span ciphertext_span(single_byte_ciphertext.data(), single_byte_ciphertext.size()); - auto encrypt_result = wrapper->Encrypt(plaintext_span, ciphertext_span); + auto encrypt_result = wrapper->Encrypt(plaintext_span); EXPECT_NE(encrypt_result, nullptr); - span ciphertext_span_const(single_byte_ciphertext.data(), single_byte_ciphertext.size()); - auto decrypt_result = wrapper->Decrypt(ciphertext_span_const); + auto ciphertext_span = encrypt_result->ciphertext(); + auto decrypt_result = wrapper->Decrypt(ciphertext_span); EXPECT_NE(decrypt_result, nullptr); EXPECT_TRUE(mock_companion_->WasEncryptCalled()); diff --git a/cpp/src/parquet/encryption/external/dbpa_test_agent.cc b/cpp/src/parquet/encryption/external/dbpa_test_agent.cc index 576611566abd..57fe79bb326e 100644 --- a/cpp/src/parquet/encryption/external/dbpa_test_agent.cc +++ b/cpp/src/parquet/encryption/external/dbpa_test_agent.cc @@ -1,84 +1,106 @@ //TODO: figure out the licensing. -#include "parquet/encryption/external/dbpa_test_agent.h" +#include #include +#include #include -#include +#include +#include +#include #include "parquet/exception.h" -#include "arrow/util/span.h" +#include "parquet/encryption/external/dbpa_test_agent.h" +#include "parquet/encryption/external/dbpa_interface.h" +#include "span.hpp" -using ::arrow::util::span; +using tcb::span; +using dbps::external::EncryptionResult; +using dbps::external::DecryptionResult; namespace parquet::encryption::external { -DBPATestAgent::DBPATestAgent() - : agent_name_(""), configuration_(""), enable_logging_(false), is_initialized_(false) { - std::cout << "Created DBPATestAgent with empty constructor" << std::endl; -} - -void DBPATestAgent::init(std::string agent_name, - std::string configuration, - bool enable_logging) { - std::cout << "Inside DBPATestAgent::init" << std::endl; - - agent_name_ = agent_name; - configuration_ = configuration; - enable_logging_ = enable_logging; - - if (enable_logging_) { - std::cout << "DBPATestAgent initialized with name: " << agent_name_ - << ", config: " << configuration_ << std::endl; - } +// Concrete implementation of EncryptionResult for testing +class TestEncryptionResult : public EncryptionResult { +public: + TestEncryptionResult(std::vector data, bool success = true, + std::string error_msg = "", + std::map error_fields = {}) + : ciphertext_data_(std::move(data)), success_(success), + error_message_(std::move(error_msg)), error_fields_(std::move(error_fields)) {} + + span ciphertext() const override { + return span(ciphertext_data_.data(), ciphertext_data_.size()); + } + + std::size_t size() const override { return static_cast(ciphertext_data_.size()); } + bool success() const override { return success_; } + const std::string& error_message() const override { return error_message_; } + const std::map& error_fields() const override { return error_fields_; } + +private: + std::vector ciphertext_data_; + bool success_; + std::string error_message_; + std::map error_fields_; +}; + +// Concrete implementation of DecryptionResult for testing +class TestDecryptionResult : public DecryptionResult { +public: + TestDecryptionResult(std::vector data, bool success = true, + std::string error_msg = "", + std::map error_fields = {}) + : plaintext_data_(std::move(data)), success_(success), + error_message_(std::move(error_msg)), error_fields_(std::move(error_fields)) {} + + span plaintext() const override { + return span(plaintext_data_.data(), plaintext_data_.size()); + } + + std::size_t size() const override { return plaintext_data_.size(); } + bool success() const override { return success_; } + const std::string& error_message() const override { return error_message_; } + const std::map& error_fields() const override { return error_fields_; } + +private: + std::vector plaintext_data_; + bool success_; + std::string error_message_; + std::map error_fields_; +}; + +DBPATestAgent::DBPATestAgent() { } std::unique_ptr DBPATestAgent::Encrypt( - span plaintext, - span ciphertext) { + span plaintext) { - if (enable_logging_) { - std::cout << "Encrypting " << plaintext.size() << " bytes" << std::endl; - } - // Simple XOR encryption for testing purposes // In a real implementation, this would use proper encryption - if (ciphertext.size() < plaintext.size()) { - throw std::runtime_error("Ciphertext buffer too small"); - } - + std::vector ciphertext_data(plaintext.size()); + for (size_t i = 0; i < plaintext.size(); ++i) { - ciphertext[i] = plaintext[i] ^ 0xAA; // Simple XOR with 0xAA + ciphertext_data[i] = plaintext[i] ^ 0xAA; // Simple XOR with 0xAA } - auto result = std::make_unique(); - if (enable_logging_) { - std::cout << "Encryption completed successfully" << std::endl; - } - - return result; + return std::make_unique(std::move(ciphertext_data)); } std::unique_ptr DBPATestAgent::Decrypt( span ciphertext) { - if (enable_logging_) { - std::cout << "Decrypting " << ciphertext.size() << " bytes" << std::endl; - } - - // For this test implementation, we're not actually decrypting + // Simple XOR decryption for testing purposes // In a real implementation, this would perform actual decryption - auto result = std::make_unique(); - if (enable_logging_) { - std::cout << "Decryption completed successfully" << std::endl; - } + std::vector plaintext_data(ciphertext.size()); - return result; + for (size_t i = 0; i < ciphertext.size(); ++i) { + plaintext_data[i] = ciphertext[i] ^ 0xAA; // Simple XOR with 0xAA + } + + return std::make_unique(std::move(plaintext_data)); } DBPATestAgent::~DBPATestAgent() { - if (enable_logging_) { - std::cout << "Destroying DBPATestAgent: " << agent_name_ << std::endl; - } } // Export function for creating new instances from shared library @@ -88,4 +110,4 @@ extern "C" { } } -} // namespace parquet::encryption::external +} // namespace parquet::encryption::external diff --git a/cpp/src/parquet/encryption/external/dbpa_test_agent.h b/cpp/src/parquet/encryption/external/dbpa_test_agent.h index 3d7fd07bb38b..065a6745af97 100644 --- a/cpp/src/parquet/encryption/external/dbpa_test_agent.h +++ b/cpp/src/parquet/encryption/external/dbpa_test_agent.h @@ -5,35 +5,43 @@ #include #include -#include "parquet/platform.h" #include "parquet/encryption/external/dbpa_interface.h" +#include "span.hpp" + +using tcb::span; + +using dbps::external::DataBatchProtectionAgentInterface; +using dbps::external::EncryptionResult; +using dbps::external::DecryptionResult; +using dbps::external::Type; +using dbps::external::CompressionCodec; namespace parquet::encryption::external { // Implementation of the DataBatchProtectionAgentInterface for testing purposes. -// It is not used in production. -class PARQUET_EXPORT DBPATestAgent : public DataBatchProtectionAgentInterface { +// It is used to test library wrapper/loading code. +// Will never be used in production. +class DBPATestAgent : public DataBatchProtectionAgentInterface { public: explicit DBPATestAgent(); - void init(std::string agent_name, - std::string configuration, - bool enable_logging = true); + void init( + std::string column_name, + std::map connection_config, + std::string app_context, + std::string column_key_id, + Type::type data_type, + CompressionCodec::type compression_type) override { + // init() intentionally left blank + } std::unique_ptr Encrypt( - span plaintext, - span ciphertext) override; + span plaintext) override; std::unique_ptr Decrypt( span ciphertext) override; ~DBPATestAgent(); - - private: - std::string agent_name_; - std::string configuration_; - bool enable_logging_; - bool is_initialized_; }; } // namespace parquet::encryption::external diff --git a/cpp/src/parquet/encryption/external/enums.h b/cpp/src/parquet/encryption/external/enums.h new file mode 100644 index 000000000000..270580c4fc35 --- /dev/null +++ b/cpp/src/parquet/encryption/external/enums.h @@ -0,0 +1,57 @@ +//TODO: figure out the licensing. + +#pragma once + +// TODO: this file was copied from +// https://github.com/protegrity/DataBatchProtectionService +// we need to find a better way to share it between repos. + +namespace dbps::external { + +// Captures the data type of the data batch elements. +// Intentionally similar to parquet::Type to ease mapping and for compatibility with a known enum. +struct Type { + enum type { + BOOLEAN = 0, + INT32 = 1, + INT64 = 2, + INT96 = 3, + FLOAT = 4, + DOUBLE = 5, + BYTE_ARRAY = 6, + FIXED_LEN_BYTE_ARRAY = 7 + }; +}; + +// Intentionally similar to arrow::CompressionCodec +struct CompressionCodec { + enum type { + UNCOMPRESSED = 0, + SNAPPY = 1, + GZIP = 2, + LZO = 3, + BROTLI = 4, + LZ4 = 5, + ZSTD = 6, + LZ4_RAW = 7 + }; +}; + +// Format for data values +struct Format { + enum type { + JSON = 0, + CSV = 1, + RAW_C_DATA = 2 + }; +}; + +// Encoding applied to the data when serialized to send over the wire +struct Encoding { + enum type { + UTF8 = 0, + BASE64 = 1 + }; +}; + +} diff --git a/cpp/src/parquet/encryption/external/loadable_encryptor_utils.cc b/cpp/src/parquet/encryption/external/loadable_encryptor_utils.cc index 33b8a2018a3f..54477825d03d 100644 --- a/cpp/src/parquet/encryption/external/loadable_encryptor_utils.cc +++ b/cpp/src/parquet/encryption/external/loadable_encryptor_utils.cc @@ -4,7 +4,6 @@ #include "parquet/encryption/external/dbpa_interface.h" #include "parquet/encryption/external/dbpa_library_wrapper.h" -#include "arrow/util/span.h" #include "arrow/util/io_util.h" //utils for loading shared libraries #include "arrow/result.h" @@ -13,7 +12,6 @@ #include #include -using ::arrow::util::span; using ::arrow::Result; namespace parquet::encryption::external { @@ -34,7 +32,7 @@ std::unique_ptr CreateInstance(void* library_ //create_instance_fn is a function pointer to the create_new_instance function in the shared library. create_encryptor_t create_instance_fn = reinterpret_cast(symbol_result.ValueOrDie()); - //at this point, we have the create_instance function pointer (from the shared library) + // at this point, we have the create_instance function pointer (from the shared library) // so we can create a new instance of the DataBatchProtectionAgentInterface DataBatchProtectionAgentInterface* instance = create_instance_fn(); @@ -51,6 +49,7 @@ std::unique_ptr CreateInstance(void* library_ std::unique_ptr LoadableEncryptorUtils::LoadFromLibrary(const std::string& library_path) { + //TODO: remove this. std::cout << "Inside LoadableEncryptorUtils::LoadFromLibrary" << std::endl; if (library_path.empty()) { diff --git a/cpp/src/parquet/encryption/external/loadable_encryptor_utils.h b/cpp/src/parquet/encryption/external/loadable_encryptor_utils.h index 7fb55d45acbc..01deb48e4a5f 100644 --- a/cpp/src/parquet/encryption/external/loadable_encryptor_utils.h +++ b/cpp/src/parquet/encryption/external/loadable_encryptor_utils.h @@ -8,6 +8,8 @@ #include "parquet/platform.h" #include "parquet/encryption/external/dbpa_interface.h" +using dbps::external::DataBatchProtectionAgentInterface; + namespace parquet::encryption::external { class PARQUET_EXPORT LoadableEncryptorUtils { diff --git a/cpp/src/parquet/encryption/external/loadable_encryptor_utils_test.cc b/cpp/src/parquet/encryption/external/loadable_encryptor_utils_test.cc index a8ea204aba48..af18559120e0 100644 --- a/cpp/src/parquet/encryption/external/loadable_encryptor_utils_test.cc +++ b/cpp/src/parquet/encryption/external/loadable_encryptor_utils_test.cc @@ -21,8 +21,6 @@ #include #endif -using ::arrow::util::span; - namespace parquet::encryption::external::test { // Test fixture for LoadableEncryptorUtils tests diff --git a/cpp/src/parquet/encryption/external/span.hpp b/cpp/src/parquet/encryption/external/span.hpp new file mode 100644 index 000000000000..fdc3a988a4c2 --- /dev/null +++ b/cpp/src/parquet/encryption/external/span.hpp @@ -0,0 +1,618 @@ + +/* +This is an implementation of C++20's std::span +http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4820.pdf +*/ + +// Copyright Tristan Brindle 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file ../../LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#ifndef TCB_SPAN_HPP_INCLUDED +#define TCB_SPAN_HPP_INCLUDED + +#include +#include +#include +#include + +#ifndef TCB_SPAN_NO_EXCEPTIONS +// Attempt to discover whether we're being compiled with exception support +#if !(defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) +#define TCB_SPAN_NO_EXCEPTIONS +#endif +#endif + +#ifndef TCB_SPAN_NO_EXCEPTIONS +#include +#include +#endif + +// Various feature test macros + +#ifndef TCB_SPAN_NAMESPACE_NAME +#define TCB_SPAN_NAMESPACE_NAME tcb +#endif + +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define TCB_SPAN_HAVE_CPP17 +#endif + +#if __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +#define TCB_SPAN_HAVE_CPP14 +#endif + +namespace TCB_SPAN_NAMESPACE_NAME { + +// Establish default contract checking behavior +#if !defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) && \ + !defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) && \ + !defined(TCB_SPAN_NO_CONTRACT_CHECKING) +#if defined(NDEBUG) || !defined(TCB_SPAN_HAVE_CPP14) +#define TCB_SPAN_NO_CONTRACT_CHECKING +#else +#define TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION +#endif +#endif + +#if defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) +struct contract_violation_error : std::logic_error { + explicit contract_violation_error(const char* msg) : std::logic_error(msg) + {} +}; + +inline void contract_violation(const char* msg) +{ + throw contract_violation_error(msg); +} + +#elif defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) +[[noreturn]] inline void contract_violation(const char* /*unused*/) +{ + std::terminate(); +} +#endif + +#if !defined(TCB_SPAN_NO_CONTRACT_CHECKING) +#define TCB_SPAN_STRINGIFY(cond) #cond +#define TCB_SPAN_EXPECT(cond) \ + cond ? (void) 0 : contract_violation("Expected " TCB_SPAN_STRINGIFY(cond)) +#else +#define TCB_SPAN_EXPECT(cond) +#endif + +#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_inline_variables) +#define TCB_SPAN_INLINE_VAR inline +#else +#define TCB_SPAN_INLINE_VAR +#endif + +#if defined(TCB_SPAN_HAVE_CPP14) || \ + (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) +#define TCB_SPAN_HAVE_CPP14_CONSTEXPR +#endif + +#if defined(TCB_SPAN_HAVE_CPP14_CONSTEXPR) +#define TCB_SPAN_CONSTEXPR14 constexpr +#else +#define TCB_SPAN_CONSTEXPR14 +#endif + +#if defined(TCB_SPAN_HAVE_CPP14_CONSTEXPR) && \ + (!defined(_MSC_VER) || _MSC_VER > 1900) +#define TCB_SPAN_CONSTEXPR_ASSIGN constexpr +#else +#define TCB_SPAN_CONSTEXPR_ASSIGN +#endif + +#if defined(TCB_SPAN_NO_CONTRACT_CHECKING) +#define TCB_SPAN_CONSTEXPR11 constexpr +#else +#define TCB_SPAN_CONSTEXPR11 TCB_SPAN_CONSTEXPR14 +#endif + +#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_deduction_guides) +#define TCB_SPAN_HAVE_DEDUCTION_GUIDES +#endif + +#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_byte) +#define TCB_SPAN_HAVE_STD_BYTE +#endif + +#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_array_constexpr) +#define TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC +#endif + +#if defined(TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC) +#define TCB_SPAN_ARRAY_CONSTEXPR constexpr +#else +#define TCB_SPAN_ARRAY_CONSTEXPR +#endif + +#ifdef TCB_SPAN_HAVE_STD_BYTE +using byte = std::byte; +#else +using byte = unsigned char; +#endif + +#if defined(TCB_SPAN_HAVE_CPP17) +#define TCB_SPAN_NODISCARD [[nodiscard]] +#else +#define TCB_SPAN_NODISCARD +#endif + +TCB_SPAN_INLINE_VAR constexpr std::size_t dynamic_extent = SIZE_MAX; + +template +class span; + +namespace detail { + +template +struct span_storage { + constexpr span_storage() noexcept = default; + + constexpr span_storage(E* p_ptr, std::size_t /*unused*/) noexcept + : ptr(p_ptr) + {} + + E* ptr = nullptr; + static constexpr std::size_t size = S; +}; + +template +struct span_storage { + constexpr span_storage() noexcept = default; + + constexpr span_storage(E* p_ptr, std::size_t p_size) noexcept + : ptr(p_ptr), size(p_size) + {} + + E* ptr = nullptr; + std::size_t size = 0; +}; + +// Reimplementation of C++17 std::size() and std::data() +#if defined(TCB_SPAN_HAVE_CPP17) || \ + defined(__cpp_lib_nonmember_container_access) +using std::data; +using std::size; +#else +template +constexpr auto size(const C& c) -> decltype(c.size()) +{ + return c.size(); +} + +template +constexpr std::size_t size(const T (&)[N]) noexcept +{ + return N; +} + +template +constexpr auto data(C& c) -> decltype(c.data()) +{ + return c.data(); +} + +template +constexpr auto data(const C& c) -> decltype(c.data()) +{ + return c.data(); +} + +template +constexpr T* data(T (&array)[N]) noexcept +{ + return array; +} + +template +constexpr const E* data(std::initializer_list il) noexcept +{ + return il.begin(); +} +#endif // TCB_SPAN_HAVE_CPP17 + +#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_void_t) +using std::void_t; +#else +template +using void_t = void; +#endif + +template +using uncvref_t = + typename std::remove_cv::type>::type; + +template +struct is_span : std::false_type {}; + +template +struct is_span> : std::true_type {}; + +template +struct is_std_array : std::false_type {}; + +template +struct is_std_array> : std::true_type {}; + +template +struct has_size_and_data : std::false_type {}; + +template +struct has_size_and_data())), + decltype(detail::data(std::declval()))>> + : std::true_type {}; + +template > +struct is_container { + static constexpr bool value = + !is_span::value && !is_std_array::value && + !std::is_array::value && has_size_and_data::value; +}; + +template +using remove_pointer_t = typename std::remove_pointer::type; + +template +struct is_container_element_type_compatible : std::false_type {}; + +template +struct is_container_element_type_compatible< + T, E, + typename std::enable_if< + !std::is_same< + typename std::remove_cv()))>::type, + void>::value && + std::is_convertible< + remove_pointer_t()))> (*)[], + E (*)[]>::value + >::type> + : std::true_type {}; + +template +struct is_complete : std::false_type {}; + +template +struct is_complete : std::true_type {}; + +} // namespace detail + +template +class span { + static_assert(std::is_object::value, + "A span's ElementType must be an object type (not a " + "reference type or void)"); + static_assert(detail::is_complete::value, + "A span's ElementType must be a complete type (not a forward " + "declaration)"); + static_assert(!std::is_abstract::value, + "A span's ElementType cannot be an abstract class type"); + + using storage_type = detail::span_storage; + +public: + // constants and types + using element_type = ElementType; + using value_type = typename std::remove_cv::type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = element_type*; + using const_pointer = const element_type*; + using reference = element_type&; + using const_reference = const element_type&; + using iterator = pointer; + using reverse_iterator = std::reverse_iterator; + + static constexpr size_type extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + template < + std::size_t E = Extent, + typename std::enable_if<(E == dynamic_extent || E <= 0), int>::type = 0> + constexpr span() noexcept + {} + + TCB_SPAN_CONSTEXPR11 span(pointer ptr, size_type count) + : storage_(ptr, count) + { + TCB_SPAN_EXPECT(extent == dynamic_extent || count == extent); + } + + TCB_SPAN_CONSTEXPR11 span(pointer first_elem, pointer last_elem) + : storage_(first_elem, last_elem - first_elem) + { + TCB_SPAN_EXPECT(extent == dynamic_extent || + last_elem - first_elem == + static_cast(extent)); + } + + template ::value, + int>::type = 0> + constexpr span(element_type (&arr)[N]) noexcept : storage_(arr, N) + {} + + template &, ElementType>::value, + int>::type = 0> + TCB_SPAN_ARRAY_CONSTEXPR span(std::array& arr) noexcept + : storage_(arr.data(), N) + {} + + template &, ElementType>::value, + int>::type = 0> + TCB_SPAN_ARRAY_CONSTEXPR span(const std::array& arr) noexcept + : storage_(arr.data(), N) + {} + + template < + typename Container, std::size_t E = Extent, + typename std::enable_if< + E == dynamic_extent && detail::is_container::value && + detail::is_container_element_type_compatible< + Container&, ElementType>::value, + int>::type = 0> + constexpr span(Container& cont) + : storage_(detail::data(cont), detail::size(cont)) + {} + + template < + typename Container, std::size_t E = Extent, + typename std::enable_if< + E == dynamic_extent && detail::is_container::value && + detail::is_container_element_type_compatible< + const Container&, ElementType>::value, + int>::type = 0> + constexpr span(const Container& cont) + : storage_(detail::data(cont), detail::size(cont)) + {} + + constexpr span(const span& other) noexcept = default; + + template ::value, + int>::type = 0> + constexpr span(const span& other) noexcept + : storage_(other.data(), other.size()) + {} + + ~span() noexcept = default; + + TCB_SPAN_CONSTEXPR_ASSIGN span& + operator=(const span& other) noexcept = default; + + // [span.sub], span subviews + template + TCB_SPAN_CONSTEXPR11 span first() const + { + TCB_SPAN_EXPECT(Count <= size()); + return {data(), Count}; + } + + template + TCB_SPAN_CONSTEXPR11 span last() const + { + TCB_SPAN_EXPECT(Count <= size()); + return {data() + (size() - Count), Count}; + } + + template + using subspan_return_t = + span; + + template + TCB_SPAN_CONSTEXPR11 subspan_return_t subspan() const + { + TCB_SPAN_EXPECT(Offset <= size() && + (Count == dynamic_extent || Offset + Count <= size())); + return {data() + Offset, + Count != dynamic_extent ? Count : size() - Offset}; + } + + TCB_SPAN_CONSTEXPR11 span + first(size_type count) const + { + TCB_SPAN_EXPECT(count <= size()); + return {data(), count}; + } + + TCB_SPAN_CONSTEXPR11 span + last(size_type count) const + { + TCB_SPAN_EXPECT(count <= size()); + return {data() + (size() - count), count}; + } + + TCB_SPAN_CONSTEXPR11 span + subspan(size_type offset, size_type count = dynamic_extent) const + { + TCB_SPAN_EXPECT(offset <= size() && + (count == dynamic_extent || offset + count <= size())); + return {data() + offset, + count == dynamic_extent ? size() - offset : count}; + } + + // [span.obs], span observers + constexpr size_type size() const noexcept { return storage_.size; } + + constexpr size_type size_bytes() const noexcept + { + return size() * sizeof(element_type); + } + + TCB_SPAN_NODISCARD constexpr bool empty() const noexcept + { + return size() == 0; + } + + // [span.elem], span element access + TCB_SPAN_CONSTEXPR11 reference operator[](size_type idx) const + { + TCB_SPAN_EXPECT(idx < size()); + return *(data() + idx); + } + + TCB_SPAN_CONSTEXPR11 reference front() const + { + TCB_SPAN_EXPECT(!empty()); + return *data(); + } + + TCB_SPAN_CONSTEXPR11 reference back() const + { + TCB_SPAN_EXPECT(!empty()); + return *(data() + (size() - 1)); + } + + constexpr pointer data() const noexcept { return storage_.ptr; } + + // [span.iterators], span iterator support + constexpr iterator begin() const noexcept { return data(); } + + constexpr iterator end() const noexcept { return data() + size(); } + + TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rbegin() const noexcept + { + return reverse_iterator(end()); + } + + TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rend() const noexcept + { + return reverse_iterator(begin()); + } + +private: + storage_type storage_{}; +}; + +#ifdef TCB_SPAN_HAVE_DEDUCTION_GUIDES + +/* Deduction Guides */ +template +span(T (&)[N])->span; + +template +span(std::array&)->span; + +template +span(const std::array&)->span; + +template +span(Container&)->span()))>::type>; + +template +span(const Container&)->span; + +#endif // TCB_HAVE_DEDUCTION_GUIDES + +template +constexpr span +make_span(span s) noexcept +{ + return s; +} + +template +constexpr span make_span(T (&arr)[N]) noexcept +{ + return {arr}; +} + +template +TCB_SPAN_ARRAY_CONSTEXPR span make_span(std::array& arr) noexcept +{ + return {arr}; +} + +template +TCB_SPAN_ARRAY_CONSTEXPR span +make_span(const std::array& arr) noexcept +{ + return {arr}; +} + +template +constexpr span()))>::type> +make_span(Container& cont) +{ + return {cont}; +} + +template +constexpr span +make_span(const Container& cont) +{ + return {cont}; +} + +template +span +as_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template < + class ElementType, size_t Extent, + typename std::enable_if::value, int>::type = 0> +span +as_writable_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template +constexpr auto get(span s) -> decltype(s[N]) +{ + return s[N]; +} + +} // namespace TCB_SPAN_NAMESPACE_NAME + +namespace std { + +template +class tuple_size> + : public integral_constant {}; + +template +class tuple_size>; // not defined + +template +class tuple_element> { +public: + static_assert(Extent != TCB_SPAN_NAMESPACE_NAME::dynamic_extent && + I < Extent, + ""); + using type = ElementType; +}; + +} // end namespace std + +#endif // TCB_SPAN_HPP_INCLUDED