From a8a200ef26ef35dd378fd83550a72b1d962df983 Mon Sep 17 00:00:00 2001 From: Kan Tang Date: Sun, 21 Jun 2020 10:55:41 +0800 Subject: [PATCH] Added convenience layer for datalake service (#178) --- CMakeLists.txt | 2 +- README.md | 2 +- sdk/storage/CMakeLists.txt | 29 +- sdk/storage/external/json.hpp | 3504 ++++++++--- sdk/storage/inc/common/constant.hpp | 16 + .../inc/common/shared_request_options.hpp | 24 + sdk/storage/inc/common/storage_credential.hpp | 3 + sdk/storage/inc/common/storage_error.hpp | 38 + .../inc/common/storage_url_builder.hpp | 4 +- .../inc/common/token_credential_policy.hpp | 35 + sdk/storage/inc/datalake/datalake.hpp | 9 + sdk/storage/inc/datalake/datalake_options.hpp | 943 +++ .../inc/datalake/datalake_utilities.hpp | 22 + .../inc/datalake/file_system_client.hpp | 140 + sdk/storage/inc/datalake/path_client.hpp | 249 + .../protocol/datalake_rest_client.hpp | 5222 ++++++++++------- sdk/storage/inc/datalake/service_client.hpp | 82 + sdk/storage/sample/CMakeLists.txt | 5 +- .../sample/datalake_getting_started.cpp | 12 + sdk/storage/src/common/storage_error.cpp | 95 + .../src/datalake/datalake_utilities.cpp | 72 + .../src/datalake/file_system_client.cpp | 235 + sdk/storage/src/datalake/path_client.cpp | 414 ++ sdk/storage/src/datalake/service_client.cpp | 174 + sdk/storage/test/CMakeLists.txt | 2 +- 25 files changed, 8275 insertions(+), 3058 deletions(-) create mode 100644 sdk/storage/inc/common/constant.hpp create mode 100644 sdk/storage/inc/common/shared_request_options.hpp create mode 100644 sdk/storage/inc/common/storage_error.hpp create mode 100644 sdk/storage/inc/common/token_credential_policy.hpp create mode 100644 sdk/storage/inc/datalake/datalake.hpp create mode 100644 sdk/storage/inc/datalake/datalake_options.hpp create mode 100644 sdk/storage/inc/datalake/datalake_utilities.hpp create mode 100644 sdk/storage/inc/datalake/file_system_client.hpp create mode 100644 sdk/storage/inc/datalake/path_client.hpp create mode 100644 sdk/storage/inc/datalake/service_client.hpp create mode 100644 sdk/storage/src/common/storage_error.cpp create mode 100644 sdk/storage/src/datalake/datalake_utilities.cpp create mode 100644 sdk/storage/src/datalake/file_system_client.cpp create mode 100644 sdk/storage/src/datalake/path_client.cpp create mode 100644 sdk/storage/src/datalake/service_client.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a79e801e..e56ac4d4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # SPDX-License-Identifier: MIT -cmake_minimum_required (VERSION 3.12) +cmake_minimum_required (VERSION 3.15) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake-modules") # Compile Options diff --git a/README.md b/README.md index b8b3bf7bb..5cd7fd322 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ To get started with a specific library, see the **README.md** file located in th ### Prerequisites -> CMake version 3.12 is required to build these libraries +> CMake version 3.15 is required to build these libraries ## Latest Release diff --git a/sdk/storage/CMakeLists.txt b/sdk/storage/CMakeLists.txt index 22ee80667..c10c478d4 100644 --- a/sdk/storage/CMakeLists.txt +++ b/sdk/storage/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # SPDX-License-Identifier: MIT -cmake_minimum_required (VERSION 3.12) +cmake_minimum_required (VERSION 3.15) project (azure-storage LANGUAGES CXX) set(CMAKE_CXX_STANDARD 14) @@ -30,8 +30,23 @@ set (AZURE_STORAGE_BLOB_HEADER ) set (AZURE_STORAGE_DATALAKE_HEADER - external/json.hpp + inc/common/storage_common.hpp + inc/common/xml_wrapper.hpp + inc/common/storage_error.hpp + inc/common/storage_credential.hpp + inc/common/storage_url_builder.hpp + inc/common/common_headers_request_policy.hpp + inc/common/shared_key_policy.hpp + inc/common/token_credential_policy.hpp + inc/common/crypt.hpp + inc/common/constant.hpp + inc/blobs/internal/protocol/blob_rest_client.hpp inc/datalake/protocol/datalake_rest_client.hpp + inc/datalake/datalake_options.hpp + inc/datalake/datalake_utilities.hpp + inc/datalake/service_client.hpp + inc/datalake/file_system_client.hpp + inc/datalake/path_client.hpp ) set (AZURE_STORAGE_BLOB_SOURCE @@ -39,6 +54,7 @@ set (AZURE_STORAGE_BLOB_SOURCE src/common/storage_url_builder.cpp src/common/common_headers_request_policy.cpp src/common/shared_key_policy.cpp + src/common/storage_error.cpp src/common/crypt.cpp src/common/xml_wrapper.cpp src/blobs/blob_service_client.cpp @@ -50,6 +66,11 @@ set (AZURE_STORAGE_BLOB_SOURCE ) set (AZURE_STORAGE_DATALAKE_SOURCE + src/common/storage_credential.cpp + src/datalake/service_client.cpp + src/datalake/file_system_client.cpp + src/datalake/path_client.cpp + src/datalake/datalake_utilities.cpp ) set(AZURE_STORAGE_HEADER @@ -66,13 +87,15 @@ add_library(azure-storage ${AZURE_STORAGE_HEADER} ${AZURE_STORAGE_SOURCE}) find_package(LibXml2 REQUIRED) -target_include_directories(azure-storage PUBLIC ${LIBXML2_INCLUDE_DIR} $ $) +target_include_directories(azure-storage PUBLIC ${LIBXML2_INCLUDE_DIR} $ $ $) target_link_libraries(azure-storage azure-core ${LIBXML2_LIBRARIES}) if(MSVC) target_link_libraries(azure-storage bcrypt) target_compile_definitions(azure-storage PRIVATE NOMINMAX) + # C28020 and C28204 are introduced by nlohmann/json + target_compile_options(azure-storage PUBLIC /wd28204 /wd28020) else() find_package(OpenSSL REQUIRED) target_link_libraries(azure-storage OpenSSL::SSL OpenSSL::Crypto) diff --git a/sdk/storage/external/json.hpp b/sdk/storage/external/json.hpp index 06da81532..cc822a543 100644 --- a/sdk/storage/external/json.hpp +++ b/sdk/storage/external/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.7.3 +| | |__ | | | | | | version 3.8.0 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . @@ -31,12 +31,11 @@ SOFTWARE. #define INCLUDE_NLOHMANN_JSON_HPP_ #define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 7 -#define NLOHMANN_JSON_VERSION_PATCH 3 +#define NLOHMANN_JSON_VERSION_MINOR 8 +#define NLOHMANN_JSON_VERSION_PATCH 0 #include // all_of, find, for_each #include // assert -#include // and, not, or #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list @@ -58,7 +57,6 @@ SOFTWARE. #include // transform #include // array -#include // and, not #include // forward_list #include // inserter, front_inserter, end #include // map @@ -69,6 +67,16 @@ SOFTWARE. #include // pair, declval #include // valarray +// #include + + +// Header is removed in C++20. +// See for more information. + +#if __cplusplus <= 201703L + #include // and, not, or +#endif + // #include @@ -122,11 +130,11 @@ struct position_t * SPDX-License-Identifier: CC0-1.0 */ -#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 11) +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 13) #if defined(JSON_HEDLEY_VERSION) #undef JSON_HEDLEY_VERSION #endif -#define JSON_HEDLEY_VERSION 11 +#define JSON_HEDLEY_VERSION 13 #if defined(JSON_HEDLEY_STRINGIFY_EX) #undef JSON_HEDLEY_STRINGIFY_EX @@ -148,6 +156,16 @@ struct position_t #endif #define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + #if defined(JSON_HEDLEY_VERSION_ENCODE) #undef JSON_HEDLEY_VERSION_ENCODE #endif @@ -323,9 +341,17 @@ struct position_t #if defined(JSON_HEDLEY_TI_VERSION) #undef JSON_HEDLEY_TI_VERSION #endif -#if defined(__TI_COMPILER_VERSION__) +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif +#endif #if defined(JSON_HEDLEY_TI_VERSION_CHECK) #undef JSON_HEDLEY_TI_VERSION_CHECK @@ -336,6 +362,102 @@ struct position_t #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) #endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + #if defined(JSON_HEDLEY_CRAY_VERSION) #undef JSON_HEDLEY_CRAY_VERSION #endif @@ -450,6 +572,12 @@ struct position_t !defined(JSON_HEDLEY_PGI_VERSION) && \ !defined(JSON_HEDLEY_ARM_VERSION) && \ !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ !defined(__COMPCERT__) #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION #endif @@ -509,6 +637,7 @@ struct position_t #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) #elif \ !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) @@ -674,14 +803,85 @@ struct position_t #if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ #endif -#if defined(__cplusplus) && JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ xpr \ JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) #else -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP \ +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) #endif #if \ @@ -692,7 +892,13 @@ struct position_t JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ @@ -725,7 +931,13 @@ struct position_t #elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") -#elif JSON_HEDLEY_TI_VERSION_CHECK(8,1,0) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") #elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) @@ -749,7 +961,18 @@ struct position_t #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") #elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) -#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") @@ -776,7 +999,13 @@ struct position_t #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") #elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) -#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") @@ -799,8 +1028,13 @@ struct position_t #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") -#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") #else #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES #endif @@ -824,7 +1058,10 @@ struct position_t #if defined(JSON_HEDLEY_DEPRECATED_FOR) #undef JSON_HEDLEY_DEPRECATED_FOR #endif -#if defined(__cplusplus) && (__cplusplus >= 201402L) +#if JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) #elif \ @@ -834,20 +1071,30 @@ struct position_t JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,3,0) + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) #elif \ JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) - #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) @@ -876,21 +1123,40 @@ struct position_t #if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) #undef JSON_HEDLEY_WARN_UNUSED_RESULT #endif -#if defined(__cplusplus) && (__cplusplus >= 201703L) +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) #elif \ JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) #elif defined(_Check_return_) /* SAL */ #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ #else #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) #endif #if defined(JSON_HEDLEY_SENTINEL) @@ -923,14 +1189,23 @@ struct position_t JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(18,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(17,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") #elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") #elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) @@ -955,31 +1230,6 @@ struct position_t #if defined(JSON_HEDLEY_UNREACHABLE_RETURN) #undef JSON_HEDLEY_UNREACHABLE_RETURN #endif -#if \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) - #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) - #define JSON_HEDLEY_UNREACHABLE() __assume(0) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) - #if defined(__cplusplus) - #define JSON_HEDLEY_UNREACHABLE() std::_nassert(0) - #else - #define JSON_HEDLEY_UNREACHABLE() _nassert(0) - #endif - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return value -#elif defined(EXIT_FAILURE) - #define JSON_HEDLEY_UNREACHABLE() abort() -#else - #define JSON_HEDLEY_UNREACHABLE() - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return value -#endif -#if !defined(JSON_HEDLEY_UNREACHABLE_RETURN) - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() -#endif - #if defined(JSON_HEDLEY_ASSUME) #undef JSON_HEDLEY_ASSUME #endif @@ -989,20 +1239,45 @@ struct position_t #define JSON_HEDLEY_ASSUME(expr) __assume(expr) #elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) #if defined(__cplusplus) #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) #else #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) #endif -#elif \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && !defined(JSON_HEDLEY_ARM_VERSION)) || \ +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) - #define JSON_HEDLEY_ASSUME(expr) ((void) ((expr) ? 1 : (__builtin_unreachable(), 1))) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif #else - #define JSON_HEDLEY_ASSUME(expr) ((void) (expr)) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) #endif JSON_HEDLEY_DIAGNOSTIC_PUSH @@ -1046,8 +1321,17 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) #elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) @@ -1080,19 +1364,16 @@ JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_UNPREDICTABLE #endif #if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) - #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable(!!(expr)) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) #endif #if \ JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \ JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) -# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(expr, value, probability) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1, probability) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0, probability) -# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) -# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) -#if !defined(JSON_HEDLEY_BUILTIN_UNPREDICTABLE) - #define JSON_HEDLEY_BUILTIN_UNPREDICTABLE(expr) __builtin_expect_with_probability(!!(expr), 1, 0.5) -#endif +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) #elif \ JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ @@ -1100,24 +1381,31 @@ JSON_HEDLEY_DIAGNOSTIC_POP (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) # define JSON_HEDLEY_PREDICT(expr, expected, probability) \ - (((probability) >= 0.9) ? __builtin_expect(!!(expr), (expected)) : (((void) (expected)), !!(expr))) + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) # define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ (__extension__ ({ \ - JSON_HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ + double hedley_probability_ = (probability); \ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ })) # define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ (__extension__ ({ \ - JSON_HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ + double hedley_probability_ = (probability); \ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ })) # define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) # define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) #else -# define JSON_HEDLEY_PREDICT(expr, expected, probability) (((void) (expected)), !!(expr)) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) # define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) # define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) # define JSON_HEDLEY_LIKELY(expr) (!!(expr)) @@ -1137,8 +1425,17 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") @@ -1152,22 +1449,36 @@ JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_PURE #endif #if \ - JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_PURE __attribute__((__pure__)) + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) - #define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") -#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) - #define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") #else - #define JSON_HEDLEY_PURE +# define JSON_HEDLEY_PURE #endif #if defined(JSON_HEDLEY_CONST) @@ -1180,8 +1491,17 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) #define JSON_HEDLEY_CONST __attribute__((__const__)) #elif \ @@ -1203,7 +1523,10 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ defined(__clang__) @@ -1228,7 +1551,12 @@ JSON_HEDLEY_DIAGNOSTIC_POP #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_INLINE __inline #else #define JSON_HEDLEY_INLINE @@ -1238,23 +1566,40 @@ JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_ALWAYS_INLINE #endif #if \ - JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) - #define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE #elif JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) - #define JSON_HEDLEY_ALWAYS_INLINE __forceinline -#elif JSON_HEDLEY_TI_VERSION_CHECK(7,0,0) && defined(__cplusplus) - #define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") #else - #define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE #endif #if defined(JSON_HEDLEY_NEVER_INLINE) @@ -1267,14 +1612,23 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) #elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) #elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") -#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") @@ -1296,26 +1650,31 @@ JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_IMPORT #endif #if defined(_WIN32) || defined(__CYGWIN__) - #define JSON_HEDLEY_PRIVATE - #define JSON_HEDLEY_PUBLIC __declspec(dllexport) - #define JSON_HEDLEY_IMPORT __declspec(dllimport) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) #else - #if \ - JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_EABI__) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) - #define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) - #define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) - #else - #define JSON_HEDLEY_PRIVATE - #define JSON_HEDLEY_PUBLIC - #endif - #define JSON_HEDLEY_IMPORT extern +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern #endif #if defined(JSON_HEDLEY_NO_THROW) @@ -1337,7 +1696,9 @@ JSON_HEDLEY_DIAGNOSTIC_POP #if defined(JSON_HEDLEY_FALL_THROUGH) #undef JSON_HEDLEY_FALL_THROUGH #endif -#if JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(fallthrough,7,0,0) && !defined(JSON_HEDLEY_PGI_VERSION) +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) #elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) @@ -1394,7 +1755,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) @@ -1415,7 +1776,11 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) #endif # elif \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(JSON_HEDLEY_SUNPRO_VERSION) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ @@ -1431,7 +1796,12 @@ JSON_HEDLEY_DIAGNOSTIC_POP defined(JSON_HEDLEY_GCC_VERSION) || \ defined(JSON_HEDLEY_INTEL_VERSION) || \ defined(JSON_HEDLEY_TINYC_VERSION) || \ - defined(JSON_HEDLEY_TI_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ defined(__clang__) # define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ sizeof(void) != \ @@ -1489,59 +1859,12 @@ JSON_HEDLEY_DIAGNOSTIC_POP # define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) #elif \ (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ - JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ - (defined(__cplusplus) && JSON_HEDLEY_TI_VERSION_CHECK(8,3,0)) + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) # define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) #else # define JSON_HEDLEY_STATIC_ASSERT(expr, message) #endif -#if defined(JSON_HEDLEY_CONST_CAST) - #undef JSON_HEDLEY_CONST_CAST -#endif -#if defined(__cplusplus) -# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) -#elif \ - JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ - ((T) (expr)); \ - JSON_HEDLEY_DIAGNOSTIC_POP \ - })) -#else -# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_REINTERPRET_CAST) - #undef JSON_HEDLEY_REINTERPRET_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) -#else - #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (*((T*) &(expr))) -#endif - -#if defined(JSON_HEDLEY_STATIC_CAST) - #undef JSON_HEDLEY_STATIC_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) -#else - #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_CPP_CAST) - #undef JSON_HEDLEY_CPP_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_CPP_CAST(T, expr) static_cast(expr) -#else - #define JSON_HEDLEY_CPP_CAST(T, expr) (expr) -#endif - #if defined(JSON_HEDLEY_NULL) #undef JSON_HEDLEY_NULL #endif @@ -1593,7 +1916,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_DIAGNOSTIC_POP #elif \ JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) #elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) # define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) @@ -1817,12 +2141,13 @@ JSON_HEDLEY_DIAGNOSTIC_POP class StringType, class BooleanType, class NumberIntegerType, \ class NumberUnsignedType, class NumberFloatType, \ template class AllocatorType, \ - template class JSONSerializer> + template class JSONSerializer, \ + class BinaryType> #define NLOHMANN_BASIC_JSON_TPL \ basic_json + AllocatorType, JSONSerializer, BinaryType> namespace nlohmann @@ -2178,10 +2503,12 @@ class other_error : public exception // #include -#include // not #include // size_t #include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +// #include + + namespace nlohmann { namespace detail @@ -2243,11 +2570,12 @@ constexpr T static_const::value; // #include -#include // not #include // numeric_limits #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval +// #include + // #include @@ -2328,7 +2656,7 @@ struct iterator_traits::value>> // #include -// http://en.cppreference.com/w/cpp/experimental/is_detected +// https://en.cppreference.com/w/cpp/experimental/is_detected namespace nlohmann { namespace detail @@ -2417,7 +2745,8 @@ template class ObjectType = class NumberFloatType = double, template class AllocatorType = std::allocator, template class JSONSerializer = - adl_serializer> + adl_serializer, + class BinaryType = std::vector> class basic_json; /*! @@ -2478,6 +2807,19 @@ template struct is_basic_json : std::false_type {}; NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json : std::true_type {}; +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + ////////////////////////// // aliases for detected // ////////////////////////// @@ -2814,11 +3156,13 @@ struct is_constructible_tuple> : conjunction // array -#include // and #include // size_t #include // uint8_t #include // string +// #include + + namespace nlohmann { namespace detail @@ -2861,24 +3205,29 @@ enum class value_t : std::uint8_t number_integer, ///< number value (signed integer) number_unsigned, ///< number value (unsigned integer) number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function }; /*! @brief comparison operator for JSON types Returns an ordering that is similar to Python: -- order: null < boolean < number < object < array < string +- order: null < boolean < number < object < array < string < binary - furthermore, each type is not smaller than itself - discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. @since version 1.0.0 */ inline bool operator<(const value_t lhs, const value_t rhs) noexcept { - static constexpr std::array order = {{ + static constexpr std::array order = {{ 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */ + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ } }; @@ -3099,9 +3448,9 @@ template ::value and not is_constructible_object_type::value and not is_constructible_string_type::value and + not std::is_same::value and not is_basic_json::value, int > = 0 > - auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) -> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), j.template get(), @@ -3116,6 +3465,17 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +template +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + } + + bin = *j.template get_ptr(); +} + template::value, int> = 0> void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) @@ -3263,7 +3623,6 @@ constexpr const auto& from_json = detail::static_const::va #include // copy -#include // or, and, not #include // begin, end #include // string #include // tuple, get @@ -3272,6 +3631,8 @@ constexpr const auto& from_json = detail::static_const::va #include // valarray #include // vector +// #include + // #include @@ -3511,6 +3872,28 @@ struct external_constructor } }; +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + { + j.m_type = value_t::binary; + typename BasicJsonType::binary_t value{b}; + j.m_value = value; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + { + j.m_type = value_t::binary; + typename BasicJsonType::binary_t value{std::move(b)}; + j.m_value = value; + j.assert_invariant(); + } +}; + template<> struct external_constructor { @@ -3700,9 +4083,9 @@ void to_json(BasicJsonType& j, const std::vector& e) template ::value and - not is_compatible_object_type< - BasicJsonType, CompatibleArrayType>::value and + not is_compatible_object_type::value and not is_compatible_string_type::value and + not std::is_same::value and not is_basic_json::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleArrayType& arr) @@ -3710,6 +4093,12 @@ void to_json(BasicJsonType& j, const CompatibleArrayType& arr) external_constructor::construct(j, arr); } +template +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor::construct(j, bin); +} + template::value, int> = 0> void to_json(BasicJsonType& j, const std::valarray& arr) @@ -3834,6 +4223,176 @@ struct adl_serializer } // namespace nlohmann +// #include + + +#include // uint8_t +#include // tie +#include // move + +namespace nlohmann +{ + +/*! +@brief an internal type for a backed binary type + +This type extends the template parameter @a BinaryType provided to `basic_json` +with a subtype used by BSON and MessagePack. This type exists so that the user +does not have to specify a type themselves with a specific naming scheme in +order to override the binary type. + +@tparam BinaryType container to store bytes (`std::vector` by + default) + +@since version 3.8.0 +*/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + /// the type of the underlying container + using container_type = BinaryType; + + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + byte_container_with_subtype(const container_type& b, std::uint8_t subtype) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype) + , m_has_subtype(true) + {} + + byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype) + , m_has_subtype(true) + {} + + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + + /*! + @brief sets the binary subtype + + Sets the binary subtype of the value, also flags a binary JSON value as + having a subtype, which has implications for serialization. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void set_subtype(std::uint8_t subtype) noexcept + { + m_subtype = subtype; + m_has_subtype = true; + } + + /*! + @brief return the binary subtype + + Returns the numerical subtype of the value if it has a subtype. If it does + not have a subtype, this function will return size_t(-1) as a sentinel + value. + + @return the numerical subtype of the binary value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + constexpr std::uint8_t subtype() const noexcept + { + return m_subtype; + } + + /*! + @brief return whether the value has a subtype + + @return whether the value has a subtype + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + + @since version 3.8.0 + */ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /*! + @brief clears the binary subtype + + Clears the binary subtype and flags the value as not having a subtype, which + has implications for serialization; for instance MessagePack will prefer the + bin family over the ext family. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + std::uint8_t m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann + +// #include + // #include // #include @@ -3890,34 +4449,15 @@ enum class input_format_t { json, cbor, msgpack, ubjson, bson }; // input adapters // //////////////////// -/*! -@brief abstract input adapter interface - -Produces a stream of std::char_traits::int_type characters from a -std::istream, a buffer, or some other input type. Accepts the return of -exactly one non-EOF character for future input. The int_type characters -returned consist of all valid char values as positive values (typically -unsigned char), plus an EOF value outside that range, specified by the value -of the function std::char_traits::eof(). This value is typically -1, but -could be any arbitrary value which is not a valid char value. -*/ -struct input_adapter_protocol -{ - /// get a character [0,255] or std::char_traits::eof(). - virtual std::char_traits::int_type get_character() = 0; - virtual ~input_adapter_protocol() = default; -}; - -/// a type to simplify interfaces -using input_adapter_t = std::shared_ptr; - /*! Input adapter for stdio file access. This adapter read only 1 byte and do not use any buffer. This adapter is a very low level adapter. */ -class file_input_adapter : public input_adapter_protocol +class file_input_adapter { public: + using char_type = char; + JSON_HEDLEY_NON_NULL(2) explicit file_input_adapter(std::FILE* f) noexcept : m_file(f) @@ -3927,10 +4467,9 @@ class file_input_adapter : public input_adapter_protocol file_input_adapter(const file_input_adapter&) = delete; file_input_adapter(file_input_adapter&&) = default; file_input_adapter& operator=(const file_input_adapter&) = delete; - file_input_adapter& operator=(file_input_adapter&&) = default; - ~file_input_adapter() override = default; + file_input_adapter& operator=(file_input_adapter&&) = delete; - std::char_traits::int_type get_character() noexcept override + std::char_traits::int_type get_character() noexcept { return std::fgetc(m_file); } @@ -3950,92 +4489,111 @@ characters following those used in parsing the JSON input. Clears the std::istream flags; any input errors (e.g., EOF) will be detected by the first subsequent call for input from the std::istream. */ -class input_stream_adapter : public input_adapter_protocol +class input_stream_adapter { public: - ~input_stream_adapter() override + using char_type = char; + + ~input_stream_adapter() { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags, except eof - is.clear(is.rdstate() & std::ios::eofbit); + if (is) + { + is->clear(is->rdstate() & std::ios::eofbit); + } } explicit input_stream_adapter(std::istream& i) - : is(i), sb(*i.rdbuf()) + : is(&i), sb(i.rdbuf()) {} // delete because of pointer members input_stream_adapter(const input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&) = delete; - input_stream_adapter(input_stream_adapter&&) = delete; - input_stream_adapter& operator=(input_stream_adapter&&) = delete; + input_stream_adapter& operator=(input_stream_adapter&& rhs) = delete; + + input_stream_adapter(input_stream_adapter&& rhs) : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } // std::istream/std::streambuf use std::char_traits::to_int_type, to // ensure that std::char_traits::eof() and the character 0xFF do not // end up as the same value, eg. 0xFFFFFFFF. - std::char_traits::int_type get_character() override + std::char_traits::int_type get_character() { - auto res = sb.sbumpc(); + auto res = sb->sbumpc(); // set eof manually, as we don't use the istream interface. - if (res == EOF) + if (JSON_HEDLEY_UNLIKELY(res == EOF)) { - is.clear(is.rdstate() | std::ios::eofbit); + is->clear(is->rdstate() | std::ios::eofbit); } return res; } private: /// the associated input stream - std::istream& is; - std::streambuf& sb; + std::istream* is = nullptr; + std::streambuf* sb = nullptr; }; -/// input adapter for buffer input -class input_buffer_adapter : public input_adapter_protocol +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template +class iterator_input_adapter { public: - input_buffer_adapter(const char* b, const std::size_t l) noexcept - : cursor(b), limit(b == nullptr ? nullptr : (b + l)) - {} + using char_type = typename std::iterator_traits::value_type; - // delete because of pointer members - input_buffer_adapter(const input_buffer_adapter&) = delete; - input_buffer_adapter& operator=(input_buffer_adapter&) = delete; - input_buffer_adapter(input_buffer_adapter&&) = delete; - input_buffer_adapter& operator=(input_buffer_adapter&&) = delete; - ~input_buffer_adapter() override = default; + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) {} - std::char_traits::int_type get_character() noexcept override + typename std::char_traits::int_type get_character() { - if (JSON_HEDLEY_LIKELY(cursor < limit)) + if (JSON_HEDLEY_LIKELY(current != end)) { - assert(cursor != nullptr and limit != nullptr); - return std::char_traits::to_int_type(*(cursor++)); + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + else + { + return std::char_traits::eof(); } - - return std::char_traits::eof(); } private: - /// pointer to the current character - const char* cursor; - /// pointer past the last character - const char* const limit; + IteratorType current; + IteratorType end; + + template + friend struct wide_string_input_helper; + + bool empty() const + { + return current == end; + } + }; -template -struct wide_string_input_helper + +template +struct wide_string_input_helper; + +template +struct wide_string_input_helper { // UTF-32 - static void fill_buffer(const WideStringType& str, - size_t& current_wchar, + static void fill_buffer(BaseInputAdapter& input, std::array::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled) { utf8_bytes_index = 0; - if (current_wchar == str.size()) + if (JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; @@ -4043,7 +4601,7 @@ struct wide_string_input_helper else { // get the current character - const auto wc = static_cast(str[current_wchar++]); + const auto wc = input.get_character(); // UTF-32 to UTF-8 encoding if (wc < 0x80) @@ -4053,23 +4611,23 @@ struct wide_string_input_helper } else if (wc <= 0x7FF) { - utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u) & 0x1Fu)); - utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 2; } else if (wc <= 0xFFFF) { - utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u) & 0x0Fu)); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 3; } else if (wc <= 0x10FFFF) { - utf8_bytes[0] = static_cast::int_type>(0xF0u | ((wc >> 18u) & 0x07u)); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 12u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); - utf8_bytes[3] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 4; } else @@ -4082,19 +4640,18 @@ struct wide_string_input_helper } }; -template -struct wide_string_input_helper +template +struct wide_string_input_helper { // UTF-16 - static void fill_buffer(const WideStringType& str, - size_t& current_wchar, + static void fill_buffer(BaseInputAdapter& input, std::array::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled) { utf8_bytes_index = 0; - if (current_wchar == str.size()) + if (JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; @@ -4102,7 +4659,7 @@ struct wide_string_input_helper else { // get the current character - const auto wc = static_cast(str[current_wchar++]); + const auto wc = input.get_character(); // UTF-16 to UTF-8 encoding if (wc < 0x80) @@ -4112,23 +4669,23 @@ struct wide_string_input_helper } else if (wc <= 0x7FF) { - utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u))); - utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 2; } else if (0xD800 > wc or wc >= 0xE000) { - utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u))); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 3; } else { - if (current_wchar < str.size()) + if (JSON_HEDLEY_UNLIKELY(not input.empty())) { - const auto wc2 = static_cast(str[current_wchar++]); - const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + const auto wc2 = static_cast(input.get_character()); + const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); @@ -4137,8 +4694,6 @@ struct wide_string_input_helper } else { - // unknown character - ++current_wchar; utf8_bytes[0] = static_cast::int_type>(wc); utf8_bytes_filled = 1; } @@ -4147,20 +4702,22 @@ struct wide_string_input_helper } }; -template -class wide_string_input_adapter : public input_adapter_protocol +// Wraps another input apdater to convert wide character types into individual bytes. +template +class wide_string_input_adapter { public: - explicit wide_string_input_adapter(const WideStringType& w) noexcept - : str(w) - {} + using char_type = char; - std::char_traits::int_type get_character() noexcept override + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} + + typename std::char_traits::int_type get_character() noexcept { // check if buffer needs to be filled if (utf8_bytes_index == utf8_bytes_filled) { - fill_buffer(); + fill_buffer(); assert(utf8_bytes_filled > 0); assert(utf8_bytes_index == 0); @@ -4173,18 +4730,14 @@ class wide_string_input_adapter : public input_adapter_protocol } private: + BaseInputAdapter base_adapter; + template void fill_buffer() { - wide_string_input_helper::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); } - /// the wstring to process - const WideStringType& str; - - /// index of the current wchar in str - std::size_t current_wchar = 0; - /// a buffer for UTF-8 bytes std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; @@ -4194,112 +4747,131 @@ class wide_string_input_adapter : public input_adapter_protocol std::size_t utf8_bytes_filled = 0; }; -class input_adapter + +template +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } +}; + +template +struct is_iterator_of_multibyte +{ + using value_type = typename std::iterator_traits::value_type; + enum + { + value = sizeof(value_type) > 1 + }; +}; + +template +struct iterator_input_adapter_factory::value>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } +}; + +// General purpose iterator-based input +template +typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); +} + +// Convenience shorthand from container to iterator +template +auto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container))) +{ + // Enable ADL + using std::begin; + using std::end; + + return input_adapter(begin(container), end(container)); +} + +// Special cases with fast paths +inline file_input_adapter input_adapter(std::FILE* file) +{ + return file_input_adapter(file); +} + +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} + +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} + +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer::value and + not std::is_array::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) +{ + auto length = std::strlen(reinterpret_cast(b)); + auto ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); +} + +template +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) +{ + return input_adapter(array, array + N); +} + +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitely casted +// to the correct adapter. +class span_input_adapter { public: - // native support - JSON_HEDLEY_NON_NULL(2) - input_adapter(std::FILE* file) - : ia(std::make_shared(file)) {} - /// input adapter for input stream - input_adapter(std::istream& i) - : ia(std::make_shared(i)) {} - - /// input adapter for input stream - input_adapter(std::istream&& i) - : ia(std::make_shared(i)) {} - - input_adapter(const std::wstring& ws) - : ia(std::make_shared>(ws)) {} - - input_adapter(const std::u16string& ws) - : ia(std::make_shared>(ws)) {} - - input_adapter(const std::u32string& ws) - : ia(std::make_shared>(ws)) {} - - /// input adapter for buffer template::value and std::is_integral::type>::value and sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - input_adapter(CharT b, std::size_t l) - : ia(std::make_shared(reinterpret_cast(b), l)) {} + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} - // derived support - - /// input adapter for string literal - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, - int>::type = 0> - input_adapter(CharT b) - : input_adapter(reinterpret_cast(b), - std::strlen(reinterpret_cast(b))) {} - - /// input adapter for iterator range with contiguous storage template::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0> - input_adapter(IteratorType first, IteratorType last) + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} + + contiguous_bytes_input_adapter&& get() { -#ifndef NDEBUG - // assertion to check that the iterator range is indeed contiguous, - // see http://stackoverflow.com/a/35008842/266378 for more discussion - const auto is_contiguous = std::accumulate( - first, last, std::pair(true, 0), - [&first](std::pair res, decltype(*first) val) - { - res.first &= (val == *(std::next(std::addressof(*first), res.second++))); - return res; - }).first; - assert(is_contiguous); -#endif - - // assertion to check that each element is 1 byte long - static_assert( - sizeof(typename iterator_traits::value_type) == 1, - "each element in the iterator range must have the size of 1 byte"); - - const auto len = static_cast(std::distance(first, last)); - if (JSON_HEDLEY_LIKELY(len > 0)) - { - // there is at least one element: use the address of first - ia = std::make_shared(reinterpret_cast(&(*first)), len); - } - else - { - // the address of first cannot be used: use nullptr - ia = std::make_shared(nullptr, len); - } - } - - /// input adapter for array - template - input_adapter(T (&array)[N]) - : input_adapter(std::begin(array), std::end(array)) {} - - /// input adapter for contiguous container - template::value and - std::is_base_of()))>::iterator_category>::value, - int>::type = 0> - input_adapter(const ContiguousContainer& c) - : input_adapter(std::begin(c), std::end(c)) {} - - operator input_adapter_t() - { - return ia; + return std::move(ia); } private: - /// the actual adapter - input_adapter_t ia = nullptr; + contiguous_bytes_input_adapter ia; }; } // namespace detail } // namespace nlohmann @@ -4332,14 +4904,11 @@ input. template struct json_sax { - /// type for (signed) integers using number_integer_t = typename BasicJsonType::number_integer_t; - /// type for unsigned integers using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - /// type for floating-point numbers using number_float_t = typename BasicJsonType::number_float_t; - /// type for strings using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; /*! @brief a null value was read @@ -4384,6 +4953,14 @@ struct json_sax */ virtual bool string(string_t& val) = 0; + /*! + @brief a binary string was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary. + */ + virtual bool binary(binary_t& val) = 0; + /*! @brief the beginning of an object was read @param[in] elements number of object elements or -1 if unknown @@ -4458,6 +5035,7 @@ class json_sax_dom_parser using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; /*! @param[in, out] r reference to a JSON value that is manipulated while @@ -4511,6 +5089,12 @@ class json_sax_dom_parser return true; } + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + bool start_object(std::size_t len) { ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); @@ -4640,6 +5224,7 @@ class json_sax_dom_callback_parser using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; using parser_callback_t = typename BasicJsonType::parser_callback_t; using parse_event_t = typename BasicJsonType::parse_event_t; @@ -4694,6 +5279,12 @@ class json_sax_dom_callback_parser return true; } + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + bool start_object(std::size_t len) { // check callback for object start @@ -4742,7 +5333,7 @@ class json_sax_dom_callback_parser ref_stack.pop_back(); keep_stack.pop_back(); - if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_object()) + if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_structured()) { // remove discarded value for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) @@ -4944,6 +5535,7 @@ class json_sax_acceptor using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; bool null() { @@ -4975,7 +5567,12 @@ class json_sax_acceptor return true; } - bool start_object(std::size_t /*unused*/ = std::size_t(-1)) + bool binary(binary_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = std::size_t(-1)) { return true; } @@ -4990,7 +5587,7 @@ class json_sax_acceptor return true; } - bool start_array(std::size_t /*unused*/ = std::size_t(-1)) + bool start_array(std::size_t /*unused*/ = std::size_t(-1)) { return true; } @@ -5164,6 +5761,20 @@ namespace nlohmann { namespace detail { + +/*! +@brief determine system byte order + +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianess(int num = 1) noexcept +{ + return *reinterpret_cast(&num) == 1; +} + + /////////////////// // binary reader // /////////////////// @@ -5171,14 +5782,17 @@ namespace detail /*! @brief deserialization of CBOR, MessagePack, and UBJSON values */ -template> +template> class binary_reader { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; public: /*! @@ -5186,10 +5800,9 @@ class binary_reader @param[in] adapter input adapter to read from */ - explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter)) + explicit binary_reader(InputAdapterType&& adapter) : ia(std::move(adapter)) { (void)detail::is_sax_static_asserts {}; - assert(ia); } // make class move-only @@ -5248,7 +5861,7 @@ class binary_reader get(); } - if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) { return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"))); @@ -5258,18 +5871,6 @@ class binary_reader return result; } - /*! - @brief determine system byte order - - @return true if and only if system's byte order is little endian - - @note from http://stackoverflow.com/a/1001328/266378 - */ - static constexpr bool little_endianess(int num = 1) noexcept - { - return *reinterpret_cast(&num) == 1; - } - private: ////////// // BSON // @@ -5318,7 +5919,7 @@ class binary_reader { return true; } - *out++ = static_cast(current); + *out++ = static_cast(current); } return true; @@ -5344,7 +5945,33 @@ class binary_reader return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"))); } - return get_string(input_format_t::bson, len - static_cast(1), result) and get() != std::char_traits::eof(); + return get_string(input_format_t::bson, len - static_cast(1), result) and get() != std::char_traits::eof(); + } + + /*! + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in, out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed + */ + template + bool get_bson_binary(const NumberType len, binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); + } + + // All BSON binary values have a subtype + std::uint8_t subtype; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); + + return get_binary(input_format_t::bson, len, result); } /*! @@ -5357,7 +5984,7 @@ class binary_reader Unsupported BSON record type 0x... @return whether a valid BSON-object/array was passed to the SAX parser */ - bool parse_bson_element_internal(const int element_type, + bool parse_bson_element_internal(const char_int_type element_type, const std::size_t element_type_parse_position) { switch (element_type) @@ -5385,6 +6012,13 @@ class binary_reader return parse_bson_array(); } + case 0x05: // binary + { + std::int32_t len; + binary_t value; + return get_number(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value); + } + case 0x08: // boolean { return sax->boolean(get() != 0); @@ -5431,7 +6065,8 @@ class binary_reader bool parse_bson_element_list(const bool is_array) { string_t key; - while (int element_type = get()) + + while (auto element_type = get()) { if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "element list"))) { @@ -5499,7 +6134,7 @@ class binary_reader switch (get_char ? get() : current) { // EOF - case std::char_traits::eof(): + case std::char_traits::eof(): return unexpect_eof(input_format_t::cbor, "value"); // Integer 0x00..0x17 (0..23) @@ -5605,6 +6240,41 @@ class binary_reader - static_cast(number)); } + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) and sax->binary(b); + } + // UTF-8 string (0x00..0x17 bytes follow) case 0x60: case 0x61: @@ -5759,12 +6429,12 @@ class binary_reader case 0xF9: // Half-Precision Float (two-byte IEEE 754) { - const int byte1_raw = get(); + const auto byte1_raw = get(); if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) { return false; } - const int byte2_raw = get(); + const auto byte2_raw = get(); if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) { return false; @@ -5920,6 +6590,105 @@ class binary_reader } } + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + { + return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x58: // Binary data (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); + } + + case 0x59: // Binary data (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); + } + + case 0x5A: // Binary data (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); + } + + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (not get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"))); + } + } + } + /*! @param[in] len the length of the array or std::size_t(-1) for an array of indefinite size @@ -6018,7 +6787,7 @@ class binary_reader switch (get()) { // EOF - case std::char_traits::eof(): + case std::char_traits::eof(): return unexpect_eof(input_format_t::msgpack, "value"); // positive fixint @@ -6240,6 +7009,22 @@ class binary_reader case 0xC3: // true return sax->boolean(true); + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) and sax->binary(b); + } + case 0xCA: // float 32 { float number; @@ -6449,6 +7234,123 @@ class binary_reader } } + /*! + @brief reads a MessagePack byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len; + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC5: // bin 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC6: // bin 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC7: // ext 8 + { + std::uint8_t len; + std::int8_t subtype; + return get_number(input_format_t::msgpack, len) and + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); + } + + case 0xC8: // ext 16 + { + std::uint16_t len; + std::int8_t subtype; + return get_number(input_format_t::msgpack, len) and + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); + } + + case 0xC9: // ext 32 + { + std::uint32_t len; + std::int8_t subtype; + return get_number(input_format_t::msgpack, len) and + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); + } + + case 0xD4: // fixext 1 + { + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 1, result) and + assign_and_return_true(subtype); + } + + case 0xD5: // fixext 2 + { + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 2, result) and + assign_and_return_true(subtype); + } + + case 0xD6: // fixext 4 + { + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 4, result) and + assign_and_return_true(subtype); + } + + case 0xD7: // fixext 8 + { + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 8, result) and + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 16, result) and + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + /*! @param[in] len the length of the array @return whether array creation completed @@ -6662,7 +7564,7 @@ class binary_reader @return whether pair creation completed */ - bool get_ubjson_size_type(std::pair& result) + bool get_ubjson_size_type(std::pair& result) { result.first = string_t::npos; // size result.second = 0; // type @@ -6703,11 +7605,11 @@ class binary_reader @param prefix the previously read or set type prefix @return whether value creation completed */ - bool get_ubjson_value(const int prefix) + bool get_ubjson_value(const char_int_type prefix) { switch (prefix) { - case std::char_traits::eof(): // EOF + case std::char_traits::eof(): // EOF return unexpect_eof(input_format_t::ubjson, "value"); case 'T': // true @@ -6772,7 +7674,7 @@ class binary_reader auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"))); } - string_t s(1, static_cast(current)); + string_t s(1, static_cast(current)); return sax->string(s); } @@ -6801,7 +7703,7 @@ class binary_reader */ bool get_ubjson_array() { - std::pair size_and_type; + std::pair size_and_type; if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) { return false; @@ -6863,7 +7765,7 @@ class binary_reader */ bool get_ubjson_object() { - std::pair size_and_type; + std::pair size_and_type; if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) { return false; @@ -6933,6 +7835,9 @@ class binary_reader return sax->end_object(); } + // Note, no reader for UBJSON binary types is implemented because they do + // not exist + /////////////////////// // Utility functions // /////////////////////// @@ -6942,20 +7847,20 @@ class binary_reader This function provides the interface to the used input adapter. It does not throw in case the input reached EOF, but returns a -'ve valued - `std::char_traits::eof()` in that case. + `std::char_traits::eof()` in that case. @return character read from the input */ - int get() + char_int_type get() { ++chars_read; - return current = ia->get_character(); + return current = ia.get_character(); } /*! @return character read from the input after ignoring all 'N' entries */ - int get_ignore_noop() + char_int_type get_ignore_noop() { do { @@ -7035,7 +7940,39 @@ class binary_reader { success = false; } - return static_cast(current); + return std::char_traits::to_char_type(current); + }); + return success; + } + + /*! + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. + */ + template + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) + { + bool success = true; + std::generate_n(std::back_inserter(result), len, [this, &success, &format]() + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "binary"))) + { + success = false; + } + return static_cast(current); }); return success; } @@ -7048,7 +7985,7 @@ class binary_reader JSON_HEDLEY_NON_NULL(3) bool unexpect_eof(const input_format_t format, const char* context) const { - if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) { return sax->parse_error(chars_read, "", parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); @@ -7105,10 +8042,10 @@ class binary_reader private: /// input adapter - input_adapter_t ia = nullptr; + InputAdapterType ia; /// the current character - int current = std::char_traits::eof(); + char_int_type current = std::char_traits::eof(); /// the number of characters read std::size_t chars_read = 0; @@ -7152,19 +8089,9 @@ namespace detail // lexer // /////////// -/*! -@brief lexical analysis - -This class organizes the lexical analysis during JSON deserialization. -*/ template -class lexer +class lexer_base { - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - public: /// token types for the parser enum class token_type @@ -7205,9 +8132,9 @@ class lexer return "null literal"; case token_type::value_string: return "string literal"; - case lexer::token_type::value_unsigned: - case lexer::token_type::value_integer: - case lexer::token_type::value_float: + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: return "number literal"; case token_type::begin_array: return "'['"; @@ -7233,15 +8160,33 @@ class lexer // LCOV_EXCL_STOP } } +}; +/*! +@brief lexical analysis - explicit lexer(detail::input_adapter_t&& adapter) - : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer : public lexer_base +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + using token_type = typename lexer_base::token_type; + + explicit lexer(InputAdapterType&& adapter) + : ia(std::move(adapter)), decimal_point_char(static_cast(get_decimal_point())) {} // delete because of pointer members lexer(const lexer&) = delete; - lexer(lexer&&) = delete; + lexer(lexer&&) = default; lexer& operator=(lexer&) = delete; - lexer& operator=(lexer&&) = delete; + lexer& operator=(lexer&&) = default; ~lexer() = default; private: @@ -7325,7 +8270,7 @@ class lexer @return true if and only if no range violation was detected */ - bool next_byte_in_range(std::initializer_list ranges) + bool next_byte_in_range(std::initializer_list ranges) { assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); add(current); @@ -7376,7 +8321,7 @@ class lexer switch (get()) { // end of file while parsing string - case std::char_traits::eof(): + case std::char_traits::eof(): { error_message = "invalid string: missing closing quote"; return token_type::parse_error; @@ -7468,13 +8413,13 @@ class lexer } else { - error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; return token_type::parse_error; } } else { - error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; return token_type::parse_error; } } @@ -7494,28 +8439,28 @@ class lexer if (codepoint < 0x80) { // 1-byte characters: 0xxxxxxx (ASCII) - add(codepoint); + add(static_cast(codepoint)); } else if (codepoint <= 0x7FF) { // 2-byte characters: 110xxxxx 10xxxxxx - add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); } else if (codepoint <= 0xFFFF) { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); } else { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); } break; @@ -7983,7 +8928,7 @@ class lexer minus | zero | any1 | [error] | [error] | [error] | [error] | [error] zero | done | done | exponent | done | done | decimal1 | done any1 | any1 | any1 | exponent | done | done | decimal1 | done - decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] decimal2 | decimal2 | decimal2 | exponent | done | done | done | done exponent | any2 | any2 | [error] | sign | sign | [error] | [error] sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] @@ -8337,13 +9282,13 @@ scan_number_done: @param[in] return_type the token type to return on success */ JSON_HEDLEY_NON_NULL(2) - token_type scan_literal(const char* literal_text, const std::size_t length, + token_type scan_literal(const char_type* literal_text, const std::size_t length, token_type return_type) { - assert(current == literal_text[0]); + assert(std::char_traits::to_char_type(current) == literal_text[0]); for (std::size_t i = 1; i < length; ++i) { - if (JSON_HEDLEY_UNLIKELY(get() != literal_text[i])) + if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) { error_message = "invalid literal"; return token_type::parse_error; @@ -8361,7 +9306,7 @@ scan_number_done: { token_buffer.clear(); token_string.clear(); - token_string.push_back(std::char_traits::to_char_type(current)); + token_string.push_back(std::char_traits::to_char_type(current)); } /* @@ -8374,7 +9319,7 @@ scan_number_done: @return character read from the input */ - std::char_traits::int_type get() + char_int_type get() { ++position.chars_read_total; ++position.chars_read_current_line; @@ -8386,12 +9331,12 @@ scan_number_done: } else { - current = ia->get_character(); + current = ia.get_character(); } - if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) { - token_string.push_back(std::char_traits::to_char_type(current)); + token_string.push_back(std::char_traits::to_char_type(current)); } if (current == '\n') @@ -8430,7 +9375,7 @@ scan_number_done: --position.chars_read_current_line; } - if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) { assert(not token_string.empty()); token_string.pop_back(); @@ -8438,9 +9383,9 @@ scan_number_done: } /// add a character to token_buffer - void add(int c) + void add(char_int_type c) { - token_buffer.push_back(std::char_traits::to_char_type(c)); + token_buffer.push_back(static_cast(c)); } public: @@ -8491,7 +9436,7 @@ scan_number_done: std::string result; for (const auto c : token_string) { - if ('\x00' <= c and c <= '\x1F') + if (static_cast(c) <= '\x1F') { // escape control characters std::array cs{{}}; @@ -8501,7 +9446,7 @@ scan_number_done: else { // add character as is - result.push_back(c); + result.push_back(static_cast(c)); } } @@ -8571,11 +9516,20 @@ scan_number_done: // literals case 't': - return scan_literal("true", 4, token_type::literal_true); + { + std::array true_literal = {{'t', 'r', 'u', 'e'}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } case 'f': - return scan_literal("false", 5, token_type::literal_false); + { + std::array false_literal = {{'f', 'a', 'l', 's', 'e'}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); + } case 'n': - return scan_literal("null", 4, token_type::literal_null); + { + std::array null_literal = {{'n', 'u', 'l', 'l'}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } // string case '\"': @@ -8598,7 +9552,7 @@ scan_number_done: // end of input (the null byte is needed when parsing from // string literals) case '\0': - case std::char_traits::eof(): + case std::char_traits::eof(): return token_type::end_of_input; // error @@ -8610,10 +9564,10 @@ scan_number_done: private: /// input adapter - detail::input_adapter_t ia = nullptr; + InputAdapterType ia; /// the current character - std::char_traits::int_type current = std::char_traits::eof(); + char_int_type current = std::char_traits::eof(); /// whether the next get() call should just return current bool next_unget = false; @@ -8622,7 +9576,7 @@ scan_number_done: position_t position {}; /// raw input token string (for error messages) - std::vector token_string {}; + std::vector token_string {}; /// buffer for variable-length tokens (numbers, strings) string_t token_buffer {}; @@ -8636,7 +9590,7 @@ scan_number_done: number_float_t value_float = 0; /// the decimal point - const char decimal_point_char = '.'; + const char_int_type decimal_point_char = '.'; }; } // namespace detail } // namespace nlohmann @@ -8675,44 +9629,45 @@ namespace detail // parser // //////////// +enum class parse_event_t : uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + /*! @brief syntax analysis -This class implements a recursive decent parser. +This class implements a recursive descent parser. */ -template +template class parser { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using lexer_t = lexer; + using lexer_t = lexer; using token_type = typename lexer_t::token_type; public: - enum class parse_event_t : uint8_t - { - /// the parser read `{` and started to process a JSON object - object_start, - /// the parser read `}` and finished processing a JSON object - object_end, - /// the parser read `[` and started to process a JSON array - array_start, - /// the parser read `]` and finished processing a JSON array - array_end, - /// the parser read a key of a value in an object - key, - /// the parser finished reading a JSON value - value - }; - - using parser_callback_t = - std::function; - /// a parser reading from an input adapter - explicit parser(detail::input_adapter_t&& adapter, - const parser_callback_t cb = nullptr, + explicit parser(InputAdapterType&& adapter, + const parser_callback_t cb = nullptr, const bool allow_exceptions_ = true) : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) { @@ -9137,7 +10092,7 @@ class parser private: /// callback function - const parser_callback_t callback = nullptr; + const parser_callback_t callback = nullptr; /// the type of the last read token token_type last_token = token_type::uninitialized; /// the lexer @@ -9290,6 +10245,8 @@ template struct internal_iterator typename BasicJsonType::object_t::iterator object_iterator {}; /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; + /// iterator for JSON binary arrays + typename BasicJsonType::binary_t::container_type::iterator binary_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; @@ -9299,10 +10256,11 @@ template struct internal_iterator // #include -#include // not #include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next #include // conditional, is_const, remove_const +// #include + // #include // #include @@ -10200,8 +11158,8 @@ class json_pointer /*! @brief append an array index at the end of this JSON pointer - @param[in] array_index array index to append - @return JSON pointer with @a array_index appended + @param[in] array_idx array index to append + @return JSON pointer with @a array_idx appended @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} @@ -10213,9 +11171,9 @@ class json_pointer @since version 3.6.0 */ - json_pointer& operator/=(std::size_t array_index) + json_pointer& operator/=(std::size_t array_idx) { - return *this /= std::to_string(array_index); + return *this /= std::to_string(array_idx); } /*! @@ -10263,8 +11221,8 @@ class json_pointer @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer @param[in] ptr JSON pointer - @param[in] array_index array index - @return a new JSON pointer with @a array_index appended to @a ptr + @param[in] array_idx array index + @return a new JSON pointer with @a array_idx appended to @a ptr @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} @@ -10274,9 +11232,9 @@ class json_pointer @since version 3.6.0 */ - friend json_pointer operator/(const json_pointer& ptr, std::size_t array_index) + friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx) { - return json_pointer(ptr) /= array_index; + return json_pointer(ptr) /= array_idx; } /*! @@ -10403,8 +11361,30 @@ class json_pointer */ static int array_index(const std::string& s) { + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + s + + "' must not begin with '0'")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and not (s[0] >= '1' and s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number")); + } + std::size_t processed_chars = 0; - const int res = std::stoi(s, &processed_chars); + int res = 0; + JSON_TRY + { + res = std::stoi(s, &processed_chars); + } + JSON_CATCH(std::out_of_range&) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } // check if the string was completely read if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) @@ -10471,14 +11451,7 @@ class json_pointer case detail::value_t::array: { // create an entry in the array - JSON_TRY - { - result = &result->operator[](static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + result = &result->operator[](static_cast(array_index(reference_token))); break; } @@ -10548,14 +11521,6 @@ class json_pointer case detail::value_t::array: { - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - if (reference_token == "-") { // explicitly treat "-" as index beyond the end @@ -10564,15 +11529,8 @@ class json_pointer else { // convert array index to number; unchecked access - JSON_TRY - { - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); } break; } @@ -10615,23 +11573,8 @@ class json_pointer ") is out of range")); } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at(static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->at(static_cast(array_index(reference_token))); break; } @@ -10680,24 +11623,9 @@ class json_pointer ") is out of range")); } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - // use unchecked array access - JSON_TRY - { - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); break; } @@ -10739,23 +11667,8 @@ class json_pointer ") is out of range")); } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at(static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->at(static_cast(array_index(reference_token))); break; } @@ -10797,31 +11710,36 @@ class json_pointer // "-" always fails the range check return false; } - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 and not ("0" <= reference_token and reference_token <= "9"))) { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); + // invalid char + return false; } - - JSON_TRY + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) { - const auto idx = static_cast(array_index(reference_token)); - if (idx >= ptr->size()) + if (JSON_HEDLEY_UNLIKELY(not ('1' <= reference_token[0] and reference_token[0] <= '9'))) { - // index out of range + // first char should be between '1' and '9' return false; } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (JSON_HEDLEY_UNLIKELY(not ('0' <= reference_token[i] and reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } - ptr = &ptr->operator[](idx); - break; - } - JSON_CATCH(std::invalid_argument&) + const auto idx = static_cast(array_index(reference_token)); + if (idx >= ptr->size()) { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + // index out of range + return false; } + + ptr = &ptr->operator[](idx); break; } @@ -11171,6 +12089,7 @@ class json_ref #include // memcpy #include // numeric_limits #include // string +#include // isnan, isinf // #include @@ -11318,6 +12237,7 @@ template class binary_writer { using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; public: /*! @@ -11468,8 +12388,35 @@ class binary_writer case value_t::number_float: { - oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); + if (std::isnan(j.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + else + { + if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and + static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and + static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) + { + oa->write_character(get_cbor_float_prefix(static_cast(j.m_value.number_float))); + write_number(static_cast(j.m_value.number_float)); + } + else + { + oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); + write_number(j.m_value.number_float); + } + } break; } @@ -11550,6 +12497,45 @@ class binary_writer break; } + case value_t::binary: + { + // step 1: write control byte and the binary array size + const auto N = j.m_value.binary->size(); + if (N <= 0x17) + { + write_number(static_cast(0x40 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x58)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x59)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + case value_t::object: { // step 1: write control byte and the object size @@ -11798,6 +12784,101 @@ class binary_writer break; } + case value_t::binary: + { + // step 0: determine if the binary type has a set subtype to + // determine whether or not to use the ext or fixext types + const bool use_ext = j.m_value.binary->has_subtype(); + + // step 1: write control byte and the byte string length + const auto N = j.m_value.binary->size(); + if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type; + bool fixed = true; + if (use_ext) + { + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } + + } + else + { + output_type = 0xC4; // bin 8 + fixed = false; + } + + oa->write_character(to_char_type(output_type)); + if (not fixed) + { + write_number(static_cast(N)); + } + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type; + if (use_ext) + { + output_type = 0xC8; // ext 16 + } + else + { + output_type = 0xC5; // bin 16 + } + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type; + if (use_ext) + { + output_type = 0xC9; // ext 32 + } + else + { + output_type = 0xC6; // bin 32 + } + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + + // step 1.5: if this is an ext type, write the subtype + if (use_ext) + { + write_number(static_cast(j.m_value.binary->subtype())); + } + + // step 2: write the byte string + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + case value_t::object: { // step 1: write control byte and the object size @@ -11941,6 +13022,49 @@ class binary_writer break; } + case value_t::binary: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + if (use_type and not j.m_value.binary->empty()) + { + assert(use_count); + oa->write_character(to_char_type('$')); + oa->write_character('U'); + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + } + + if (use_type) + { + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + j.m_value.binary->size()); + } + else + { + for (size_t i = 0; i < j.m_value.binary->size(); ++i) + { + oa->write_character(to_char_type('U')); + oa->write_character(j.m_value.binary->data()[i]); + } + } + + if (not use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + case value_t::object: { if (add_prefix) @@ -12155,7 +13279,7 @@ class binary_writer { std::size_t array_index = 0ul; - const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), 0ul, [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) + const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) { return result + calc_bson_element_size(std::to_string(array_index++), el); }); @@ -12163,6 +13287,14 @@ class binary_writer return sizeof(std::int32_t) + embedded_document_size + 1ul; } + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + /*! @brief Writes a BSON element with key @a name and array @a value */ @@ -12182,6 +13314,20 @@ class binary_writer oa->write_character(to_char_type(0x00)); } + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, + const binary_t& value) + { + write_bson_entry_header(name, 0x05); + + write_number(static_cast(value.size())); + write_number(value.has_subtype() ? value.subtype() : std::uint8_t(0x00)); + + oa->write_characters(reinterpret_cast(value.data()), value.size()); + } + /*! @brief Calculates the size necessary to serialize the JSON value @a j with its @a name @return The calculated size for the BSON document entry for @a j with the given @a name. @@ -12198,6 +13344,9 @@ class binary_writer case value_t::array: return header_size + calc_bson_array_size(*j.m_value.array); + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_value.binary); + case value_t::boolean: return header_size + 1ul; @@ -12242,6 +13391,9 @@ class binary_writer case value_t::array: return write_bson_array(name, *j.m_value.array); + case value_t::binary: + return write_bson_binary(name, *j.m_value.binary); + case value_t::boolean: return write_bson_boolean(name, j.m_value.boolean); @@ -12276,7 +13428,7 @@ class binary_writer */ static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) { - std::size_t document_size = std::accumulate(value.begin(), value.end(), 0ul, + std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0), [](size_t result, const typename BasicJsonType::object_t::value_type & el) { return result += calc_bson_element_size(el.first, el.second); @@ -12522,7 +13674,8 @@ class binary_writer case value_t::string: return 'S'; - case value_t::array: + case value_t::array: // fallthrough + case value_t::binary: return '['; case value_t::object: @@ -12592,7 +13745,7 @@ class binary_writer static CharType to_char_type(std::uint8_t x) noexcept { static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); - static_assert(std::is_pod::value, "CharType must be POD"); + static_assert(std::is_trivial::value, "CharType must be trivial"); CharType result; std::memcpy(&result, &x, sizeof(x)); return result; @@ -12618,7 +13771,7 @@ class binary_writer private: /// whether we can assume little endianess - const bool is_little_endian = binary_reader::little_endianess(); + const bool is_little_endian = little_endianess(); /// the output output_adapter_t oa = nullptr; @@ -12634,7 +13787,6 @@ class binary_writer #include // reverse, remove, fill, find, none_of #include // array #include // assert -#include // and, or #include // localeconv, lconv #include // labs, isfinite, isnan, signbit #include // size_t, ptrdiff_t @@ -12645,17 +13797,21 @@ class binary_writer #include // is_same #include // move +// #include + // #include #include // array #include // assert -#include // or, and, not #include // signbit, isfinite #include // intN_t, uintN_t #include // memcpy, memmove #include // numeric_limits #include // conditional + +// #include + // #include @@ -13639,11 +14795,11 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, // digits[000] // len <= max_exp + 2 - std::memset(buf + k, '0', static_cast(n - k)); + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); // Make it look like a floating-point number (#362, #378) buf[n + 0] = '.'; buf[n + 1] = '0'; - return buf + (n + 2); + return buf + (static_cast(n) + 2); } if (0 < n and n <= max_exp) @@ -13653,9 +14809,9 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, assert(k > n); - std::memmove(buf + (n + 1), buf + n, static_cast(k - n)); + std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); buf[n] = '.'; - return buf + (k + 1); + return buf + (static_cast(k) + 1U); } if (min_exp < n and n <= 0) @@ -13663,11 +14819,11 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, // 0.[000]digits // len <= 2 + (-min_exp - 1) + max_digits10 - std::memmove(buf + (2 + -n), buf, static_cast(k)); + std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); buf[0] = '0'; buf[1] = '.'; std::memset(buf + 2, '0', static_cast(-n)); - return buf + (2 + (-n) + k); + return buf + (2U + static_cast(-n) + static_cast(k)); } if (k == 1) @@ -13682,9 +14838,9 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, // d.igitsE+123 // len <= max_digits10 + 1 + 5 - std::memmove(buf + 2, buf + 1, static_cast(k - 1)); + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); buf[1] = '.'; - buf += 1 + k; + buf += 1 + static_cast(k); } *buf++ = 'e'; @@ -13790,6 +14946,7 @@ class serializer using number_float_t = typename BasicJsonType::number_float_t; using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; static constexpr std::uint8_t UTF8_ACCEPT = 0; static constexpr std::uint8_t UTF8_REJECT = 1; @@ -13828,13 +14985,19 @@ class serializer - strings and object keys are escaped using `escape_string()` - integer numbers are converted implicitly via `operator<<` - floating-point numbers are converted to a string using `"%g"` format + - binary values are serialized as objects containing the subtype and the + byte array - @param[in] val value to serialize - @param[in] pretty_print whether the output shall be pretty-printed - @param[in] indent_step the indent level - @param[in] current_indent the current indent level (only used internally) + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) */ - void dump(const BasicJsonType& val, const bool pretty_print, + void dump(const BasicJsonType& val, + const bool pretty_print, const bool ensure_ascii, const unsigned int indent_step, const unsigned int current_indent = 0) @@ -13981,6 +15144,79 @@ class serializer return; } + case value_t::binary: + { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (not val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"bytes\":[", 10); + + if (not val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } + } + return; + } + case value_t::boolean: { if (val.m_value.boolean) @@ -14337,7 +15573,8 @@ class serializer */ template::value or - std::is_same::value, + std::is_same::value or + std::is_same::value, int> = 0> void dump_integer(NumberType x) { @@ -14375,7 +15612,7 @@ class serializer if (is_negative) { *buffer_ptr = '-'; - abs_value = remove_sign(x); + abs_value = remove_sign(static_cast(x)); // account one more byte for the minus sign n_chars = 1 + count_digits(abs_value); @@ -14552,7 +15789,9 @@ class serializer ? (byte & 0x3fu) | (codep << 6u) : (0xFFu >> type) & (byte); - state = utf8d[256u + state * 16u + type]; + std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + assert(index < 400); + state = utf8d[index]; return state; } @@ -14640,6 +15879,9 @@ default; will be used in @ref number_integer_t) `uint64_t` by default; will be used in @ref number_unsigned_t) @tparam NumberFloatType type for JSON floating-point numbers (`double` by default; will be used in @ref number_float_t) +@tparam BinaryType type for packed binary data for compatibility with binary +serialization formats (`std::vector` by default; will be used in +@ref binary_t) @tparam AllocatorType type of the allocator to use (`std::allocator` by default) @tparam JSONSerializer the serializer to resolve internal calls to `to_json()` @@ -14694,7 +15936,7 @@ relationship: The invariants are checked by member function assert_invariant(). @internal -@note ObjectType trick from http://stackoverflow.com/a/9860911 +@note ObjectType trick from https://stackoverflow.com/a/9860911 @endinternal @see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange @@ -14710,13 +15952,15 @@ class basic_json private: template friend struct detail::external_constructor; friend ::nlohmann::json_pointer; - friend ::nlohmann::detail::parser; + + template + friend class ::nlohmann::detail::parser; friend ::nlohmann::detail::serializer; template friend class ::nlohmann::detail::iter_impl; template friend class ::nlohmann::detail::binary_writer; - template + template friend class ::nlohmann::detail::binary_reader; template friend class ::nlohmann::detail::json_sax_dom_parser; @@ -14727,8 +15971,17 @@ class basic_json using basic_json_t = NLOHMANN_BASIC_JSON_TPL; // convenience aliases for types residing in namespace detail; - using lexer = ::nlohmann::detail::lexer; - using parser = ::nlohmann::detail::parser; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + bool allow_exceptions = true + ) + { + return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions); + } using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; template @@ -14742,7 +15995,8 @@ class basic_json template using output_adapter_t = ::nlohmann::detail::output_adapter_t; - using binary_reader = ::nlohmann::detail::binary_reader; + template + using binary_reader = ::nlohmann::detail::binary_reader; template using binary_writer = ::nlohmann::detail::binary_writer; using serializer = ::nlohmann::detail::serializer; @@ -15361,6 +16615,76 @@ class basic_json */ using number_float_t = NumberFloatType; + /*! + @brief a type for a packed binary type + + This type is a type designed to carry binary data that appears in various + serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and + BSON's generic binary subtype. This type is NOT a part of standard JSON and + exists solely for compatibility with these binary types. As such, it is + simply defined as an ordered sequence of zero or more byte values. + + Additionally, as an implementation detail, the subtype of the binary data is + carried around as a `std::uint8_t`, which is compatible with both of the + binary data formats that use binary subtyping, (though the specific + numbering is incompatible with each other, and it is up to the user to + translate between them). + + [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type + as: + > Major type 2: a byte string. The string's length in bytes is represented + > following the rules for positive integers (major type 0). + + [MessagePack's documentation on the bin type + family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) + describes this type as: + > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes + > in addition to the size of the byte array. + + [BSON's specifications](http://bsonspec.org/spec.html) describe several + binary types; however, this type is intended to represent the generic binary + type which has the description: + > Generic binary subtype - This is the most commonly used binary subtype and + > should be the 'default' for drivers and tools. + + None of these impose any limitations on the internal representation other + than the basic unit of storage be some type of array whose parts are + decomposable into bytes. + + The default representation of this binary format is a + `std::vector`, which is a very common way to represent a byte + array in modern C++. + + #### Default type + + The default values for @a BinaryType is `std::vector` + + #### Storage + + Binary Arrays are stored as pointers in a @ref basic_json type. That is, + for any access to array values, a pointer of the type `binary_t*` must be + dereferenced. + + #### Notes on subtypes + + - CBOR + - Binary values are represented as byte strings. No subtypes are + supported and will be ignored when CBOR is written. + - MessagePack + - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, + or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) + is used. For other sizes, the ext family (ext8, ext16, ext32) is used. + The subtype is then added as singed 8-bit integer. + - If no subtype is given, the bin family (bin8, bin16, bin32) is used. + - BSON + - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If no subtype is given, the generic binary subtype 0x00 is used. + + @sa @ref binary -- create a binary array + + @since version 3.8.0 + */ + using binary_t = nlohmann::byte_container_with_subtype; /// @} private: @@ -15403,6 +16727,7 @@ class basic_json number | number_integer | @ref number_integer_t number | number_unsigned | @ref number_unsigned_t number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t null | null | *no value is stored* @note Variable-length types (objects, arrays, and strings) are stored as @@ -15419,6 +16744,8 @@ class basic_json array_t* array; /// string (stored with pointer to save storage) string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; /// boolean boolean_t boolean; /// number (integer) @@ -15461,6 +16788,12 @@ class basic_json break; } + case value_t::binary: + { + binary = create(); + break; + } + case value_t::boolean: { boolean = boolean_t(false); @@ -15496,7 +16829,7 @@ class basic_json object = nullptr; // silence warning, see #821 if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.7.3")); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.8.0")); // LCOV_EXCL_LINE } break; } @@ -15539,6 +16872,30 @@ class basic_json array = create(std::move(value)); } + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) + { + binary = create(std::move(value)); + } + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) + { + binary = create(std::move(value)); + } + void destroy(value_t t) noexcept { // flatten the current json_value to a heap-allocated stack @@ -15614,6 +16971,14 @@ class basic_json break; } + case value_t::binary: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + default: { break; @@ -15636,6 +17001,7 @@ class basic_json assert(m_type != value_t::object or m_value.object != nullptr); assert(m_type != value_t::array or m_value.array != nullptr); assert(m_type != value_t::string or m_value.string != nullptr); + assert(m_type != value_t::binary or m_value.binary != nullptr); } public: @@ -15658,7 +17024,7 @@ class basic_json @sa @ref parser_callback_t for more information and examples */ - using parse_event_t = typename parser::parse_event_t; + using parse_event_t = detail::parse_event_t; /*! @brief per-element parser callback type @@ -15709,7 +17075,7 @@ class basic_json @since version 1.0.0 */ - using parser_callback_t = typename parser::parser_callback_t; + using parser_callback_t = detail::parser_callback_t; ////////////////// // constructors // @@ -15734,6 +17100,7 @@ class basic_json number | `0` object | `{}` array | `[]` + binary | empty array @param[in] v the type of the value to create @@ -15805,6 +17172,12 @@ class basic_json @ref number_float_t, and all convertible number types such as `int`, `size_t`, `int64_t`, `float` or `double` can be used. - **boolean**: @ref boolean_t / `bool` can be used. + - **binary**: @ref binary_t / `std::vector` may be used, + unfortunately because string literals cannot be distinguished from binary + character arrays by the C++ type system, all types compatible with `const + char*` will be directed to the string constructor instead. This is both + for backwards compatibility, and due to the fact that a binary type is not + a standard JSON type. See the examples below. @@ -15886,6 +17259,7 @@ class basic_json using other_string_t = typename BasicJsonType::string_t; using other_object_t = typename BasicJsonType::object_t; using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; switch (val.type()) { @@ -15910,6 +17284,9 @@ class basic_json case value_t::array: JSONSerializer::to_json(*this, val.template get_ref()); break; + case value_t::binary: + JSONSerializer::to_json(*this, val.template get_ref()); + break; case value_t::null: *this = nullptr; break; @@ -16048,6 +17425,99 @@ class basic_json assert_invariant(); } + /*! + @brief explicitly create a binary array (without subtype) + + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. + + @note Note, this function exists because of the difficulty in correctly + specifying the correct template overload in the standard value ctor, as both + JSON arrays and JSON binary arrays are backed with some form of a + `std::vector`. Because JSON binary arrays are a non-standard extension it + was decided that it would be best to prevent automatic initialization of a + binary array type, for backwards compatibility and so it does not happen on + accident. + + @param[in] init container containing bytes to use as binary type + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = init; + return res; + } + + /*! + @brief explicitly create a binary array (with subtype) + + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. + + @note Note, this function exists because of the difficulty in correctly + specifying the correct template overload in the standard value ctor, as both + JSON arrays and JSON binary arrays are backed with some form of a + `std::vector`. Because JSON binary arrays are a non-standard extension it + was decided that it would be best to prevent automatic initialization of a + binary array type, for backwards compatibility and so it does not happen on + accident. + + @param[in] init container containing bytes to use as binary type + @param[in] subtype subtype to use in MessagePack and BSON + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = std::move(init); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } + /*! @brief explicitly create an array from an initializer list @@ -16303,6 +17773,12 @@ class basic_json break; } + case value_t::binary: + { + m_value = *first.m_object->m_value.binary; + break; + } + default: JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()))); @@ -16316,10 +17792,10 @@ class basic_json // other constructors and destructor // /////////////////////////////////////// - /// @private - basic_json(const detail::json_ref& ref) - : basic_json(ref.moved_or_copied()) - {} + template , + std::is_same>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} /*! @brief copy constructor @@ -16396,6 +17872,12 @@ class basic_json break; } + case value_t::binary: + { + m_value = *other.m_value.binary; + break; + } + default: break; } @@ -16540,7 +18022,11 @@ class basic_json @return string containing the serialization of the JSON value @throw type_error.316 if a string stored inside the JSON value is not - UTF-8 encoded + UTF-8 encoded and @a error_handler is set to strict + + @note Binary values are serialized as object containing two keys: + - "bytes": an array of bytes as integers + - "subtype": the subtype as integer or "null" if the binary has no subtype @complexity Linear. @@ -16555,7 +18041,8 @@ class basic_json @since version 1.0.0; indentation character @a indent_char, option @a ensure_ascii and exceptions added in version 3.0.0; error - handlers added in version 3.4.0. + handlers added in version 3.4.0; serialization of binary values added + in version 3.8.0. */ string_t dump(const int indent = -1, const char indent_char = ' ', @@ -16594,6 +18081,7 @@ class basic_json number (floating-point) | value_t::number_float object | value_t::object array | value_t::array + binary | value_t::binary discarded | value_t::discarded @complexity Constant. @@ -16636,12 +18124,13 @@ class basic_json @sa @ref is_string() -- returns whether JSON value is a string @sa @ref is_boolean() -- returns whether JSON value is a boolean @sa @ref is_number() -- returns whether JSON value is a number + @sa @ref is_binary() -- returns whether JSON value is a binary array @since version 1.0.0 */ constexpr bool is_primitive() const noexcept { - return is_null() or is_string() or is_boolean() or is_number(); + return is_null() or is_string() or is_boolean() or is_number() or is_binary(); } /*! @@ -16896,6 +18385,28 @@ class basic_json return m_type == value_t::string; } + /*! + @brief return whether value is a binary array + + This function returns true if and only if the JSON value is a binary array. + + @return `true` if type is binary array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_binary()` for all JSON + types.,is_binary} + + @since version 3.8.0 + */ + constexpr bool is_binary() const noexcept + { + return m_type == value_t::binary; + } + /*! @brief return whether value is discarded @@ -17051,6 +18562,18 @@ class basic_json return is_number_float() ? &m_value.number_float : nullptr; } + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + /*! @brief helper function to implement get_ref() @@ -17464,12 +18987,9 @@ class basic_json not std::is_same>::value and not std::is_same::value and not detail::is_basic_json::value - -#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 and not std::is_same>::value -#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) and _MSC_VER <= 1914)) +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) and _MSC_VER >= 1910 and _MSC_VER <= 1914)) and not std::is_same::value -#endif #endif and detail::is_detected::value , int >::type = 0 > @@ -17479,6 +18999,36 @@ class basic_json return get(); } + /*! + @return reference to the binary value + + @throw type_error.302 if the value is not binary + + @sa @ref is_binary() to check if the value is binary + + @since version 3.8.0 + */ + binary_t& get_binary() + { + if (not is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + + /// @copydoc get_binary() + const binary_t& get_binary() const + { + if (not is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + /// @} @@ -17999,7 +19549,8 @@ class basic_json @since version 1.0.0 */ template::value, int>::type = 0> + std::is_convertible::value + and not std::is_same::value, int>::type = 0> ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const { // at only works for objects @@ -18108,8 +19659,8 @@ class basic_json container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the - first element is returned. In case of number, string, or boolean values, a - reference to the value is returned. + first element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. @complexity Constant. @@ -18151,8 +19702,8 @@ class basic_json @endcode @return In case of a structured type (array or object), a reference to the - last element is returned. In case of number, string, or boolean values, a - reference to the value is returned. + last element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. @complexity Constant. @@ -18218,7 +19769,7 @@ class basic_json @complexity The complexity depends on the type: - objects: amortized constant - arrays: linear in distance between @a pos and the end of the container - - strings: linear in the length of the string + - strings and binary: linear in the length of the member - other types: constant @liveexample{The example shows the result of `erase()` for different JSON @@ -18254,6 +19805,7 @@ class basic_json case value_t::number_integer: case value_t::number_unsigned: case value_t::string: + case value_t::binary: { if (JSON_HEDLEY_UNLIKELY(not pos.m_it.primitive_iterator.is_begin())) { @@ -18267,6 +19819,13 @@ class basic_json std::allocator_traits::deallocate(alloc, m_value.string, 1); m_value.string = nullptr; } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } m_type = value_t::null; assert_invariant(); @@ -18324,7 +19883,7 @@ class basic_json - objects: `log(size()) + std::distance(first, last)` - arrays: linear in the distance between @a first and @a last, plus linear in the distance between @a last and end of the container - - strings: linear in the length of the string + - strings and binary: linear in the length of the member - other types: constant @liveexample{The example shows the result of `erase()` for different JSON @@ -18359,6 +19918,7 @@ class basic_json case value_t::number_integer: case value_t::number_unsigned: case value_t::string: + case value_t::binary: { if (JSON_HEDLEY_LIKELY(not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())) @@ -18373,6 +19933,13 @@ class basic_json std::allocator_traits::deallocate(alloc, m_value.string, 1); m_value.string = nullptr; } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } m_type = value_t::null; assert_invariant(); @@ -18978,7 +20545,7 @@ class basic_json future 4.0.0 of the library. Please use @ref items() instead; that is, replace `json::iterator_wrapper(j)` with `j.items()`. */ - JSON_HEDLEY_DEPRECATED(3.1.0) + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) static iteration_proxy iterator_wrapper(reference ref) noexcept { return ref.items(); @@ -18987,7 +20554,7 @@ class basic_json /*! @copydoc iterator_wrapper(reference) */ - JSON_HEDLEY_DEPRECATED(3.1.0) + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) static iteration_proxy iterator_wrapper(const_reference ref) noexcept { return ref.items(); @@ -19044,6 +20611,11 @@ class basic_json element as string (see example). For primitive types (e.g., numbers), `key()` returns an empty string. + @warning Using `items()` on temporary objects is dangerous. Make sure the + object's lifetime exeeds the iteration. See + for more + information. + @return iteration proxy object wrapping @a ref with an interface to use in range-based for loops @@ -19092,6 +20664,7 @@ class basic_json boolean | `false` string | `false` number | `false` + binary | `false` object | result of function `object_t::empty()` array | result of function `array_t::empty()` @@ -19163,6 +20736,7 @@ class basic_json boolean | `1` string | `1` number | `1` + binary | `1` object | result of function object_t::size() array | result of function array_t::size() @@ -19237,6 +20811,7 @@ class basic_json boolean | `1` (same as `size()`) string | `1` (same as `size()`) number | `1` (same as `size()`) + binary | `1` (same as `size()`) object | result of function `object_t::max_size()` array | result of function `array_t::max_size()` @@ -19309,6 +20884,7 @@ class basic_json boolean | `false` string | `""` number | `0` + binary | An empty byte vector object | `{}` array | `[]` @@ -19366,6 +20942,12 @@ class basic_json break; } + case value_t::binary: + { + m_value.binary->clear(); + break; + } + case value_t::array: { m_value.array->clear(); @@ -19421,9 +21003,7 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); - // invalidate object: mark it null so we do not call the destructor - // cppcheck-suppress accessMoved - val.m_type = value_t::null; + // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } /*! @@ -20162,6 +21742,53 @@ class basic_json } } + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other binary to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__binary_t} + + @since version 3.8.0 + */ + void swap(binary_t& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /// @copydoc swap(binary_t) + void swap(typename binary_t::container_type& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + /// @} public: @@ -20180,13 +21807,13 @@ class basic_json their stored values are the same according to their respective `operator==`. - Integer and floating-point numbers are automatically converted before - comparison. Note than two NaN values are always treated as unequal. + comparison. Note that two NaN values are always treated as unequal. - Two JSON null values are equal. @note Floating-point inside JSON values numbers are compared with `json::number_float_t::operator==` which is `double::operator==` by default. To compare floating-point while respecting an epsilon, an alternative - [comparison function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39) + [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39) could be used, for instance @code {.cpp} template::value, T>::type> @@ -20195,6 +21822,22 @@ class basic_json return std::abs(a - b) <= epsilon; } @endcode + Or you can self-defined operator equal function like this: + @code {.cpp} + bool my_equal(const_reference lhs, const_reference rhs) { + const auto lhs_type lhs.type(); + const auto rhs_type rhs.type(); + if (lhs_type == rhs_type) { + switch(lhs_type) + // self_defined case + case value_t::number_float: + return std::abs(lhs - rhs) <= std::numeric_limits::epsilon(); + // other cases remain the same with the original + ... + } + ... + } + @endcode @note NaN values never compare equal to themselves or to other NaN values. @@ -20244,6 +21887,9 @@ class basic_json case value_t::number_float: return lhs.m_value.number_float == rhs.m_value.number_float; + case value_t::binary: + return *lhs.m_value.binary == *rhs.m_value.binary; + default: return false; } @@ -20404,6 +22050,9 @@ class basic_json case value_t::number_float: return (lhs.m_value.number_float) < (rhs.m_value.number_float); + case value_t::binary: + return (*lhs.m_value.binary) < (*rhs.m_value.binary); + default: return false; } @@ -20662,7 +22311,7 @@ class basic_json instead; that is, replace calls like `j >> o;` with `o << j;`. @since version 1.0.0; deprecated since version 3.0.0 */ - JSON_HEDLEY_DEPRECATED(3.0.0) + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) friend std::ostream& operator>>(const basic_json& j, std::ostream& o) { return o << j; @@ -20681,29 +22330,13 @@ class basic_json /*! @brief deserialize from a compatible input - This function reads from a compatible input. Examples are: - - an array of 1-byte values - - strings with character/literal type with size of 1 byte - - input streams - - container with contiguous storage of 1-byte values. Compatible container - types include `std::vector`, `std::string`, `std::array`, - `std::valarray`, and `std::initializer_list`. Furthermore, C-style - arrays can be used with `std::begin()`/`std::end()`. User-defined - containers can be used as long as they implement random-access iterators - and a contiguous storage. - - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with a noncompliant container and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. @param[in] i input to read from @param[in] cb a parser callback function of type @ref parser_callback_t @@ -20723,7 +22356,7 @@ class basic_json @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @a cb or reading from the input @a i has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. @@ -20741,19 +22374,107 @@ class basic_json @since version 2.0.3 (contiguous containers) */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json parse(detail::input_adapter&& i, + static basic_json parse(InputType&& i, const parser_callback_t cb = nullptr, const bool allow_exceptions = true) { basic_json result; - parser(i, cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions).parse(true, result); return result; } - static bool accept(detail::input_adapter&& i) + /*! + @brief deserialize from a pair of character iterators + + The value_type of the iterator must be a integral type with size of 1, 2 or + 4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32. + + @param[in] first iterator to start of character range + @param[in] last iterator to end of character range + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) { - return parser(i).accept(true); + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions).parse(true, result); + return result; + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) + { + basic_json result; + parser(i.get(), cb, allow_exceptions).parse(true, result); + return result; + } + + /*! + @brief check if the input is valid JSON + + Unlike the @ref parse(InputType&&, const parser_callback_t,const bool) + function, this function neither throws an exception in case of invalid JSON + input (i.e., a parse error) nor creates diagnostic information. + + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + + @return Whether the input read from @a i is valid JSON. + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `accept()` function reading + from a string.,accept__string} + */ + template + static bool accept(InputType&& i) + { + return parser(detail::input_adapter(std::forward(i))).accept(true); + } + + template + static bool accept(IteratorType first, IteratorType last) + { + return parser(detail::input_adapter(std::move(first), std::move(last))).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i) + { + return parser(i.get()).accept(true); } /*! @@ -20762,28 +22483,12 @@ class basic_json The SAX event lister must follow the interface of @ref json_sax. This function reads from a compatible input. Examples are: - - an array of 1-byte values - - strings with character/literal type with size of 1 byte - - input streams - - container with contiguous storage of 1-byte values. Compatible container - types include `std::vector`, `std::string`, `std::array`, - `std::valarray`, and `std::initializer_list`. Furthermore, C-style - arrays can be used with `std::begin()`/`std::end()`. User-defined - containers can be used as long as they implement random-access iterators - and a contiguous storage. - - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with a noncompliant container and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. @param[in] i input to read from @param[in,out] sax SAX event listener @@ -20809,97 +22514,41 @@ class basic_json @since version 3.2.0 */ - template + template JSON_HEDLEY_NON_NULL(2) - static bool sax_parse(detail::input_adapter&& i, SAX* sax, + static bool sax_parse(InputType&& i, SAX* sax, input_format_t format = input_format_t::json, const bool strict = true) { - assert(sax); + auto ia = detail::input_adapter(std::forward(i)); return format == input_format_t::json - ? parser(std::move(i)).sax_parse(sax, strict) - : detail::binary_reader(std::move(i)).sax_parse(format, sax, strict); + ? parser(std::move(ia)).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } - /*! - @brief deserialize from an iterator range with contiguous storage - - This function reads from an iterator range of a container with contiguous - storage of 1-byte values. Compatible container types include - `std::vector`, `std::string`, `std::array`, `std::valarray`, and - `std::initializer_list`. Furthermore, C-style arrays can be used with - `std::begin()`/`std::end()`. User-defined containers can be used as long - as they implement random-access iterators and a contiguous storage. - - @pre The iterator range is contiguous. Violating this precondition yields - undefined behavior. **This precondition is enforced with an assertion.** - @pre Each element in the range has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with noncompliant iterators and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @tparam IteratorType iterator of container with contiguous storage - @param[in] first begin of the range to parse (included) - @param[in] last end of the range to parse (excluded) - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return deserialized JSON value; in case of a parse error and - @a allow_exceptions set to `false`, the return value will be - value_t::discarded. - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `parse()` function reading - from an iterator range.,parse__iteratortype__parser_callback_t} - - @since version 2.0.3 - */ - template::iterator_category>::value, int>::type = 0> - static basic_json parse(IteratorType first, IteratorType last, - const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) - { - basic_json result; - parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result); - return result; - } - - template::iterator_category>::value, int>::type = 0> - static bool accept(IteratorType first, IteratorType last) - { - return parser(detail::input_adapter(first, last)).accept(true); - } - - template::iterator_category>::value, int>::type = 0> + template JSON_HEDLEY_NON_NULL(3) - static bool sax_parse(IteratorType first, IteratorType last, SAX* sax) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true) { - return parser(detail::input_adapter(first, last)).sax_parse(sax); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia)).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + template + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true) + { + auto ia = i.get(); + return format == input_format_t::json + ? parser(std::move(ia)).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } /*! @@ -20910,7 +22559,7 @@ class basic_json instead; that is, replace calls like `j << i;` with `i >> j;`. @since version 1.0.0; deprecated since version 3.0.0 */ - JSON_HEDLEY_DEPRECATED(3.0.0) + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) friend std::istream& operator<<(basic_json& j, std::istream& i) { return operator>>(i, j); @@ -20968,6 +22617,7 @@ class basic_json number | `"number"` (for all number types) object | `"object"` array | `"array"` + binary | `"binary"` discarded | `"discarded"` @exceptionsafety No-throw guarantee: this function never throws exceptions. @@ -20999,6 +22649,8 @@ class basic_json return "string"; case value_t::boolean: return "boolean"; + case value_t::binary: + return "binary"; case value_t::discarded: return "discarded"; default: @@ -21058,7 +22710,8 @@ class basic_json number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B - number_float | *any value* | Double-Precision Float | 0xFB + number_float | *any value representable by a float* | Single-Precision Float | 0xFA + number_float | *any value NOT representable by a float* | Double-Precision Float | 0xFB string | *length*: 0..23 | UTF-8 string | 0x60..0x77 string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 @@ -21074,6 +22727,11 @@ class basic_json object | *size*: 256..65535 | map (2 bytes follow) | 0xB9 object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB + binary | *size*: 0..23 | byte string | 0x40..0x57 + binary | *size*: 23..255 | byte string (1 byte follow) | 0x58 + binary | *size*: 256..65535 | byte string (2 bytes follow) | 0x59 + binary | *size*: 65536..4294967295 | byte string (4 bytes follow) | 0x5A + binary | *size*: 4294967296..18446744073709551615 | byte string (8 bytes follow) | 0x5B @note The mapping is **complete** in the sense that any JSON value type can be converted to a CBOR value. @@ -21083,10 +22741,10 @@ class basic_json function which serializes NaN or Infinity to `null`. @note The following CBOR types are not used in the conversion: - - byte strings (0x40..0x5F) - UTF-8 strings terminated by "break" (0x7F) - arrays terminated by "break" (0x9F) - maps terminated by "break" (0xBF) + - byte strings terminated by "break" (0x5F) - date/time (0xC0..0xC1) - bignum (0xC2..0xC3) - decimal fraction (0xC4) @@ -21095,11 +22753,11 @@ class basic_json - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) - - half and single-precision floats (0xF9-0xFA) + - half-precision floats (0xF9) - break (0xFF) @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector + @return CBOR serialization as byte vector @complexity Linear in the size of the JSON value @a j. @@ -21113,7 +22771,8 @@ class basic_json @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the related UBJSON format - @since version 2.0.9 + @since version 2.0.9; compact representation of floating-point numbers + since version 3.8.0 */ static std::vector to_cbor(const basic_json& j) { @@ -21173,20 +22832,21 @@ class basic_json object | *size*: 0..15 | fix map | 0x80..0x8F object | *size*: 16..65535 | map 16 | 0xDE object | *size*: 65536..4294967295 | map 32 | 0xDF + binary | *size*: 0..255 | bin 8 | 0xC4 + binary | *size*: 256..65535 | bin 16 | 0xC5 + binary | *size*: 65536..4294967295 | bin 32 | 0xC6 @note The mapping is **complete** in the sense that any JSON value type can be converted to a MessagePack value. @note The following values can **not** be converted to a MessagePack value: - strings with more than 4294967295 bytes + - byte strings with more than 4294967295 bytes - arrays with more than 4294967295 elements - objects with more than 4294967295 elements @note The following MessagePack types are not used in the conversion: - - bin 8 - bin 32 (0xC4..0xC6) - - ext 8 - ext 32 (0xC7..0xC9) - float 32 (0xCA) - - fixext 1 - fixext 16 (0xD4..0xD8) @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. @@ -21289,6 +22949,12 @@ class basic_json the benefit of this parameter is that the receiving side is immediately informed on the number of elements of the container. + @note If the JSON data contains the binary type, the value stored is a list + of integers, as suggested by the UBJSON documentation. In particular, + this means that serialization and the deserialization of a JSON + containing binary values into UBJSON and back will result in a + different JSON object. + @param[in] j JSON value to serialize @param[in] use_size whether to add size annotations to container types @param[in] use_type whether to add type annotations to container types @@ -21353,6 +23019,7 @@ class basic_json string | *any value* | string | 0x02 array | *any value* | document | 0x04 object | *any value* | document | 0x03 + binary | *any value* | binary | 0x05 @warning The mapping is **incomplete**, since only JSON-objects (and things contained therein) can be serialized to BSON. @@ -21434,7 +23101,11 @@ class basic_json Negative integer | number_integer | 0x39 Negative integer | number_integer | 0x3A Negative integer | number_integer | 0x3B - Negative integer | number_integer | 0x40..0x57 + Byte string | binary | 0x40..0x57 + Byte string | binary | 0x58 + Byte string | binary | 0x59 + Byte string | binary | 0x5A + Byte string | binary | 0x5B UTF-8 string | string | 0x60..0x77 UTF-8 string | string | 0x78 UTF-8 string | string | 0x79 @@ -21463,7 +23134,6 @@ class basic_json @warning The mapping is **incomplete** in the sense that not all CBOR types can be converted to a JSON value. The following CBOR types are not supported and will yield parse errors (parse_error.112): - - byte strings (0x40..0x5F) - date/time (0xC0..0xC1) - bignum (0xC2..0xC3) - decimal fraction (0xC4) @@ -21513,30 +23183,56 @@ class basic_json @a strict parameter since 3.0.0; added @a allow_exceptions parameter since 3.2.0 */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_cbor(detail::input_adapter&& i, + static basic_json from_cbor(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::cbor, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /*! @copydoc from_cbor(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_cbor(A1 && a1, A2 && a2, + static basic_json from_cbor(IteratorType first, IteratorType last, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::cbor, &sdp, strict); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions); + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -21574,15 +23270,19 @@ class basic_json array 32 | array | 0xDD map 16 | object | 0xDE map 32 | object | 0xDF + bin 8 | binary | 0xC4 + bin 16 | binary | 0xC5 + bin 32 | binary | 0xC6 + ext 8 | binary | 0xC7 + ext 16 | binary | 0xC8 + ext 32 | binary | 0xC9 + fixext 1 | binary | 0xD4 + fixext 2 | binary | 0xD5 + fixext 4 | binary | 0xD6 + fixext 8 | binary | 0xD7 + fixext 16 | binary | 0xD8 negative fixint | number_integer | 0xE0-0xFF - @warning The mapping is **incomplete** in the sense that not all - MessagePack types can be converted to a JSON value. The following - MessagePack types are not supported and will yield parse errors: - - bin 8 - bin 32 (0xC4..0xC6) - - ext 8 - ext 32 (0xC7..0xC9) - - fixext 1 - fixext 16 (0xD4..0xD8) - @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. @@ -21622,33 +23322,60 @@ class basic_json @a strict parameter since 3.0.0; added @a allow_exceptions parameter since 3.2.0 */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_msgpack(detail::input_adapter&& i, + static basic_json from_msgpack(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::msgpack, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /*! @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_msgpack(A1 && a1, A2 && a2, + static basic_json from_msgpack(IteratorType first, IteratorType last, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::msgpack, &sdp, strict); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! @brief create a JSON value from an input in UBJSON format @@ -21710,33 +23437,59 @@ class basic_json @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0 */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_ubjson(detail::input_adapter&& i, + static basic_json from_ubjson(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::ubjson, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /*! @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_ubjson(A1 && a1, A2 && a2, + static basic_json from_ubjson(IteratorType first, IteratorType last, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::ubjson, &sdp, strict); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! @brief Create a JSON value from an input in BSON format @@ -21797,35 +23550,57 @@ class basic_json @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the related UBJSON format */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_bson(detail::input_adapter&& i, + static basic_json from_bson(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::bson, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /*! @copydoc from_bson(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_bson(A1 && a1, A2 && a2, + static basic_json from_bson(IteratorType first, IteratorType last, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::bson, &sdp, strict); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } - + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } /// @} ////////////////////////// @@ -21878,7 +23653,7 @@ class basic_json Uses a JSON pointer to retrieve a reference to the respective JSON value. No bound checking is performed. The function does not change the JSON - value; no `null` values are created. In particular, the the special value + value; no `null` values are created. In particular, the special value `-` yields an exception. @param[in] ptr JSON pointer to the desired element @@ -22462,7 +24237,7 @@ class basic_json result.push_back( { {"op", "add"}, - {"path", path + "/" + std::to_string(i)}, + {"path", path + "/-"}, {"value", target[i]} }); ++i; @@ -22747,7 +24522,6 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef JSON_HEDLEY_ARRAY_PARAM #undef JSON_HEDLEY_ASSUME #undef JSON_HEDLEY_BEGIN_C_DECLS -#undef JSON_HEDLEY_C_DECL #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE #undef JSON_HEDLEY_CLANG_HAS_BUILTIN #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE @@ -22758,13 +24532,16 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef JSON_HEDLEY_COMPCERT_VERSION #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK #undef JSON_HEDLEY_CONCAT +#undef JSON_HEDLEY_CONCAT3 +#undef JSON_HEDLEY_CONCAT3_EX #undef JSON_HEDLEY_CONCAT_EX #undef JSON_HEDLEY_CONST -#undef JSON_HEDLEY_CONST_CAST #undef JSON_HEDLEY_CONSTEXPR +#undef JSON_HEDLEY_CONST_CAST #undef JSON_HEDLEY_CPP_CAST #undef JSON_HEDLEY_CRAY_VERSION #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#undef JSON_HEDLEY_C_DECL #undef JSON_HEDLEY_DEPRECATED #undef JSON_HEDLEY_DEPRECATED_FOR #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL @@ -22780,7 +24557,6 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef JSON_HEDLEY_EMSCRIPTEN_VERSION #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK #undef JSON_HEDLEY_END_C_DECLS -#undef JSON_HEDLEY_FALL_THROUGH #undef JSON_HEDLEY_FLAGS #undef JSON_HEDLEY_FLAGS_CAST #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE @@ -22826,8 +24602,8 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef JSON_HEDLEY_MSVC_VERSION #undef JSON_HEDLEY_MSVC_VERSION_CHECK #undef JSON_HEDLEY_NEVER_INLINE -#undef JSON_HEDLEY_NO_ESCAPE #undef JSON_HEDLEY_NON_NULL +#undef JSON_HEDLEY_NO_ESCAPE #undef JSON_HEDLEY_NO_RETURN #undef JSON_HEDLEY_NO_THROW #undef JSON_HEDLEY_NULL @@ -22855,6 +24631,18 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK #undef JSON_HEDLEY_TINYC_VERSION #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL2000_VERSION +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL430_VERSION +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL6X_VERSION +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL7X_VERSION +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK #undef JSON_HEDLEY_TI_VERSION #undef JSON_HEDLEY_TI_VERSION_CHECK #undef JSON_HEDLEY_UNAVAILABLE @@ -22869,6 +24657,8 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef JSON_HEDLEY_VERSION_ENCODE #undef JSON_HEDLEY_WARNING #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef JSON_HEDLEY_FALL_THROUGH diff --git a/sdk/storage/inc/common/constant.hpp b/sdk/storage/inc/common/constant.hpp new file mode 100644 index 000000000..495e024d5 --- /dev/null +++ b/sdk/storage/inc/common/constant.hpp @@ -0,0 +1,16 @@ + +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +namespace Azure { namespace Storage { namespace Details { + constexpr static const char* c_ConnectionStringTagAccountName = "AccountName"; + constexpr static const char* c_ConnectionStringTagAccountKey = "AccountKey"; + constexpr static const char* c_ConnectionStringTagBlobEndpoint = "BlobEndpoint"; + constexpr static const char* c_ConnectionStringTagDataLakeEndpoint = "AdlsEndpoint"; + constexpr static const char* c_ConnectionStringTagEndpointSuffix = "EndpointSuffix"; + constexpr static const char* c_ConnectionStringTagDefaultEndpointsProtocol + = "DefaultEndpointsProtocol"; + constexpr static const char* c_DfsEndpointIdentifier = "dfs"; +}}} // namespace Azure::Storage::Details diff --git a/sdk/storage/inc/common/shared_request_options.hpp b/sdk/storage/inc/common/shared_request_options.hpp new file mode 100644 index 000000000..2700f3679 --- /dev/null +++ b/sdk/storage/inc/common/shared_request_options.hpp @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "context.hpp" + +namespace Azure { namespace Storage { + + struct SharedRequestOptions + { + /** + * @brief An optional operation timeout value in seconds. The period begins + * when the request is received by the service. If the timeout value + * elapses before the operation completes, the operation fails. + */ + int32_t Timeout = 0; // TODO: This is going to be changed into Nullable. + + /** + * @brief Context for cancelling long running operations. + */ + Azure::Core::Context Context; + }; +}} // namespace Azure::Storage diff --git a/sdk/storage/inc/common/storage_credential.hpp b/sdk/storage/inc/common/storage_credential.hpp index e25aa3584..cdc89e7c1 100644 --- a/sdk/storage/inc/common/storage_credential.hpp +++ b/sdk/storage/inc/common/storage_credential.hpp @@ -9,6 +9,8 @@ namespace Azure { namespace Storage { + class TokenCredentialPolicy; + struct TokenCredential { explicit TokenCredential(std::string token) : Token(std::move(token)) {} @@ -25,6 +27,7 @@ namespace Azure { namespace Storage { std::lock_guard guard(Mutex); return Token; } + friend class TokenCredentialPolicy; std::mutex Mutex; std::string Token; }; diff --git a/sdk/storage/inc/common/storage_error.hpp b/sdk/storage/inc/common/storage_error.hpp new file mode 100644 index 000000000..6d19df85a --- /dev/null +++ b/sdk/storage/inc/common/storage_error.hpp @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +namespace Azure { namespace Core { namespace Http { + class Response; +}}} // namespace Azure::Core::Http + +namespace Azure { namespace Storage { + + struct StorageError : public std::runtime_error + { + explicit StorageError(const std::string& message) + : std::runtime_error(message), StatusCode(), RequestId() + { + } + explicit StorageError(const std::string& message, std::string errorCode) + : std::runtime_error(message), StatusCode(std::move(errorCode)), RequestId() + { + } + explicit StorageError(const std::string& message, std::string errorCode, std::string requestId) + : std::runtime_error(message), StatusCode(std::move(errorCode)), + RequestId(std::move(requestId)) + { + } + + std::string StatusCode; + std::string RequestId; + std::map Details; + + static StorageError CreateFromResponse(/* const */ Azure::Core::Http::Response& response); + }; +}} // namespace Azure::Storage diff --git a/sdk/storage/inc/common/storage_url_builder.hpp b/sdk/storage/inc/common/storage_url_builder.hpp index bd1f71d73..467b24566 100644 --- a/sdk/storage/inc/common/storage_url_builder.hpp +++ b/sdk/storage/inc/common/storage_url_builder.hpp @@ -57,7 +57,7 @@ namespace Azure { namespace Storage { } void RemoveQuery(const std::string& key) { m_query.erase(key); } - + const std::map& GetQuery() const { return m_query; } void SetFragment(const std::string& fragment, bool do_encoding = false) @@ -67,6 +67,8 @@ namespace Azure { namespace Storage { std::string to_string() const; + std::string GetHost() const { return m_host; } + private: static std::string EncodeHost(const std::string& host); static std::string EncodePath(const std::string& path); diff --git a/sdk/storage/inc/common/token_credential_policy.hpp b/sdk/storage/inc/common/token_credential_policy.hpp new file mode 100644 index 000000000..5e7d64bf0 --- /dev/null +++ b/sdk/storage/inc/common/token_credential_policy.hpp @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common/storage_credential.hpp" +#include "http/policy.hpp" + +namespace Azure { namespace Storage { + + class TokenCredentialPolicy : public Core::Http::HttpPolicy { + public: + explicit TokenCredentialPolicy(std::shared_ptr credential) + : m_credential(std::move(credential)) + { + } + + ~TokenCredentialPolicy() override {} + + HttpPolicy* Clone() const override { return new TokenCredentialPolicy(m_credential); } + + std::unique_ptr Send( + Core::Context& ctx, + Core::Http::Request& request, + Core::Http::NextHttpPolicy nextHttpPolicy) const override + { + request.AddHeader("Authorization", "Bearer " + m_credential->GetToken()); + return nextHttpPolicy.Send(ctx, request); + } + + private: + std::shared_ptr m_credential; + }; + +}} // namespace Azure::Storage diff --git a/sdk/storage/inc/datalake/datalake.hpp b/sdk/storage/inc/datalake/datalake.hpp new file mode 100644 index 000000000..5bea81fa7 --- /dev/null +++ b/sdk/storage/inc/datalake/datalake.hpp @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "datalake/datalake_utilities.hpp" +#include "datalake/file_system_client.hpp" +#include "datalake/path_client.hpp" +#include "datalake/service_client.hpp" diff --git a/sdk/storage/inc/datalake/datalake_options.hpp b/sdk/storage/inc/datalake/datalake_options.hpp new file mode 100644 index 000000000..37a836688 --- /dev/null +++ b/sdk/storage/inc/datalake/datalake_options.hpp @@ -0,0 +1,943 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common/shared_request_options.hpp" +#include "protocol/datalake_rest_client.hpp" + +#include +#include + +namespace Azure { namespace Storage { namespace DataLake { + + /** + * @brief Service client options used to initalize DataLakeServiceClient. + */ + struct ServiceClientOptions + { + std::vector> policies; + }; + + /** + * @brief File system client options used to initalize DataLakeFileSystemClient. + */ + struct FileSystemClientOptions + { + std::vector> policies; + }; + + /** + * @brief Path client options used to initalize DataLakePathClient. + */ + struct PathClientOptions + { + std::vector> policies; + }; + + /** + * @brief Optional parameters for DataLakeServiceClient::ListFilesSystems + */ + struct ListFileSystemsOptions : public SharedRequestOptions + { + /** + * @brief Filters results to filesystems within the specified prefix. + */ + std::string Prefix; + + /** + * @brief The number of filesystems returned with each invocation is + * limited. If the number of filesystems to be returned exceeds + * this limit, a continuation token is returned in the response + * header x-ms-continuation. When a continuation token is returned + * in the response, it must be specified in a subsequent invocation + * of the list operation to continue listing the filesystems. + */ + std::string Continuation; + + /** + * @brief An optional value that specifies the maximum number of items to + * return. If omitted or greater than 5,000, the response will + * include up to 5,000 items. + */ + int32_t MaxResults = 0; + }; + + /** + * @brief Optional parameters for DataLakeFileSystemClient::Create + */ + struct FileSystemCreateOptions : public SharedRequestOptions + { + /** + * @brief User-defined metadata to be stored with the filesystem. + * Note that the string may only contain ASCII characters in the + * ISO-8859-1 character set. + */ + std::map Metadata; + }; + + /** + * @brief Optional parameters for DataLakeFileSystemClient::Delete + */ + struct FileSystemDeleteOptions : public SharedRequestOptions + { + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has been modified since the + ( specified date and time. + */ + std::string IfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has not been modified since + * the specified date and time. + */ + std::string IfUnmodifiedSince; + }; + + /** + * @brief Optional parameters for DataLakeFileSystemClient::GetMetadata + */ + struct FileSystemGetMetadataOptions : public SharedRequestOptions + { + }; + + /** + * @brief Optional parameters for DataLakeFileSystemClient::SetMetadata + */ + struct FileSystemSetMetadataOptions : public SharedRequestOptions + { + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has been modified since the + ( specified date and time. + */ + std::string IfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has not been modified since + * the specified date and time. + */ + std::string IfUnmodifiedSince; + }; + + /** + * @brief Optional parameters for DataLakeFileSystemClient::ListPaths + */ + struct ListPathsOptions : public SharedRequestOptions + { + /** + * @brief Valid only when Hierarchical Namespace is enabled for the account. + * If "true", the user identity values returned in the owner and group + * fields of each list entry will be transformed from Azure Active Directory + * Object IDs to User Principal Names. If "false", the values will be + * returned as Azure Active Directory Object IDs. The default value is false. + * Note that group and application Object IDs are not translated because they + * do not have unique friendly names. + */ + bool Upn = bool(); + + /** + * @brief The number of paths returned with each invocation is + * limited. If the number of paths to be returned exceeds + * this limit, a continuation token is returned in the response + * header x-ms-continuation. When a continuation token is returned + * in the response, it must be specified in a subsequent invocation + * of the list operation to continue listing the paths. + */ + std::string Continuation; + + /** + * @brief An optional value that specifies the maximum number of items to + * return. If omitted or greater than 5,000, the response will + * include up to 5,000 items. + */ + int32_t MaxResults = 0; + + /** + * @brief Filters results to paths within the specified directory. An error occurs + * if the directory does not exist. + */ + std::string Directory; + }; + + /** + * @brief Optional parameters for DataLakePathClient::AppendData + */ + struct PathAppendDataOptions : public SharedRequestOptions + { + /** + * @brief Specify the transactional md5 for the body, to be validated by the service. + */ + std::string ContentMD5; + + /** + * @brief The lease ID must be specified if there is an active lease. + */ + std::string LeaseId; + }; + + /** + * @brief Optional parameters for DataLakePathClient::FlushData + */ + struct PathFlushDataOptions : public SharedRequestOptions + { + /** + * @brief If "true", uncommitted data is retained after the flush operation completes; + * otherwise, the uncommitted data is deleted after the flush operation. The + * default is false. Data at offsets less than the specified position are + * written to the file when flush succeeds, but this optional parameter allows + * data after the flush position to be retained for a future flush operation. + */ + bool RetainUncommittedData = bool(); + + /** + * @brief Azure Storage Events allow applications to receive notifications when files + * change. When Azure Storage Events are enabled, a file changed event is raised. + * This event has a property indicating whether this is the final change to distinguish + * the difference between an intermediate flush to a file stream and the final close of + * a file stream. The close query parameter is valid only when the action is "flush" + * and change notifications are enabled. If the value of close is "true" and the + * flush operation completes successfully, the service raises a file change notification + * with a property indicating that this is the final update (the file stream has been + * closed). If "false" a change notification is raised indicating the file has changed. + * The default is false. This query parameter is set to true by the Hadoop ABFS driver to + * indicate that the file stream has been closed." + */ + bool Close = bool(); + + /** + * @brief The service stores this value and includes it in the "Content-Md5" response header for + * "Read & Get Properties" operations. If this property is not specified on the request, + * then the property will be cleared for the file. Subsequent calls to "Read & Get + * Properties" will not return this property unless it is explicitly set on that file + * again. + */ + std::string ContentMD5; + + /** + * @brief The lease ID must be specified if there is an active lease. + */ + std::string LeaseId; + + /** + * @brief Sets the path's cache control. If specified, this property is stored with the + * path and returned with a read request. + */ + std::string CacheControl; + + /** + * @brief Sets the path's content type. If specified, this property is stored + * with the path and returned with a read request. + */ + std::string ContentType; + + /** + * @brief Sets the path's Content-Disposition header. + */ + std::string ContentDisposition; + + /** + * @brief Sets the path's content encoding. If specified, this property is stored with + * the path and returned with a read request. + */ + std::string ContentEncoding; + + /** + * @brief Set the path's content language. If specified, this property is stored with + * the path and returned with a read request. + */ + std::string ContentLanguage; + + /** + * @brief Specify an ETag value to operate only on path with a matching value. + */ + std::string IfMatch; + + /** + * @brief Specify an ETag value to operate only on path without a matching value. + */ + std::string IfNoneMatch; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has been modified since the + ( specified date and time. + */ + std::string IfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has not been modified since + * the specified date and time. + */ + std::string IfUnmodifiedSince; + }; + + /** + * @brief Optional parameters for DataLakePathClient::SetAccessControl + */ + struct SetAccessControlOptions : public SharedRequestOptions + { + /** + * @brief The lease ID must be specified if there is an active lease. + */ + std::string LeaseId; + + /** + * @brief The owner of the path or directory. + */ + std::string Owner; + + /** + * @brief The owning group of the path or directory. + */ + std::string Group; + + /** + * @brief only valid if Hierarchical Namespace is enabled for the account. Sets POSIX + * access permissions for the file owner, the file owning group, and others. + * Each class may be granted read, write, or execute permission. + * The sticky bit is also supported. Both symbolic (rwxrw-rw-) and 4-digit octal + * notation (e.g. 0766) are supported. + */ + std::string Permissions; + + /** + * @brief Sets POSIX access control rights on files and directories. The value is a + * comma-separated list of access control entries. Each access control entry (ACE) + * consists of a scope, a type, a user or group identifier, and permissions in the + * format "[scope:][type]:[id]:[permissions]". + */ + std::string Acl; + + /** + * @brief Specify an ETag value to operate only on path with a matching value. + */ + std::string IfMatch; + + /** + * @brief Specify an ETag value to operate only on path without a matching value. + */ + std::string IfNoneMatch; + + /** + * @brief Specify this header value to operate only on a path if it has been modified + * since the specified date/time. + */ + std::string IfModifiedSince; + + /** + * @brief Specify this header value to operate only on a path if it has not been modified + * since the specified date/time. + */ + std::string IfUnmodifiedSince; + }; + + /** + * @brief Optional parameters for DataLakePathClient::SetAccessControlRecursive + */ + struct SetAccessControlRecursiveOptions : public SharedRequestOptions + { + /** + * @brief When performing setAccessControlRecursive on a directory, the number of paths that + * are processed with each invocation is limited. If the number of paths to be processed + * exceeds this limit, a continuation token is returned in this response header. When a + * continuation token is returned in the response, it must be specified in a subsequent + * invocation of the setAccessControlRecursive operation to continue the + * setAccessControlRecursive operation on the directory. + */ + std::string Continuation; + + /** + * @brief It specifies the maximum number of files or directories on which the acl change will + * be applied. If omitted or greater than 2,000, the request will process up to 2,000 + * items. + */ + int32_t MaxRecords = int32_t(); + + /** + * @brief Sets POSIX access control rights on files and directories. The value is a + * comma-separated list of access control entries. Each access control entry (ACE) + * consists of a scope, a type, a user or group identifier, and permissions in the format + * "[scope:][type]:[id]:[permissions]". + */ + std::string Acl; + }; + + /** + * @brief Optional parameters for DataLakePathClient::SetProperties + */ + struct SetPathPropertiesOptions : public SharedRequestOptions + { + /** + * @brief User-defined metadata to be stored with the filesystem. + * Note that the string may only contain ASCII characters in the + * ISO-8859-1 character set. + */ + std::map Metadata; + + /** + * @brief Sets the path's cache control. If specified, this property is stored with the + * path and returned with a read request. + */ + std::string CacheControl; + + /** + * @brief Sets the path's content type. If specified, this property is stored + * with the path and returned with a read request. + */ + std::string ContentType; + + /** + * @brief Sets the path's Content-Disposition header. + */ + std::string ContentDisposition; + + /** + * @brief Sets the path's content encoding. If specified, this property is stored with + * the path and returned with a read request. + */ + std::string ContentEncoding; + + /** + * @brief Set the path's content language. If specified, this property is stored with + * the path and returned with a read request. + */ + std::string ContentLanguage; + + /** + * @brief Specify an ETag value to operate only on path with a matching value. + */ + std::string IfMatch; + + /** + * @brief Specify an ETag value to operate only on path without a matching value. + */ + std::string IfNoneMatch; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has been modified since the + ( specified date and time. + */ + std::string IfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has not been modified since + * the specified date and time. + */ + std::string IfUnmodifiedSince; + }; + + /** + * @brief Optional parameters for DataLakePathClient::Create + * @remark Some optional parameter is mandatory in certain combination. + * More details: + * https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/create + */ + struct PathCreateOptions : public SharedRequestOptions + { + /** + * @brief Required only for Create File and Create Directory. The value must be + * PathResourceType::File or PathResourceType::Directory. + */ + PathResourceType Resource = PathResourceType::Unknown; + + /** + * @brief Sets the path's cache control. If specified, this property is stored with the + * path and returned with a read request. + */ + std::string CacheControl; + + /** + * @brief Sets the path's content type. If specified, this property is stored + * with the path and returned with a read request. + */ + std::string ContentType; + + /** + * @brief Sets the path's Content-Disposition header. + */ + std::string ContentDisposition; + + /** + * @brief Sets the path's content encoding. If specified, this property is stored with + * the path and returned with a read request. + */ + std::string ContentEncoding; + + /** + * @brief Set the path's content language. If specified, this property is stored with + * the path and returned with a read request. + */ + std::string ContentLanguage; + + /** + * @brief If specified, the operation only succeeds if the resource's lease is active and + * matches this ID. + */ + std::string LeaseId; + + /** + * @brief Specify an ETag value to operate only on path with a matching value. + */ + std::string IfMatch; + + /** + * @brief Specify an ETag value to operate only on path without a matching value. + */ + std::string IfNoneMatch; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has been modified since the + ( specified date and time. + */ + std::string IfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has not been modified since + * the specified date and time. + */ + std::string IfUnmodifiedSince; + + /** + * @brief User-defined metadata to be stored with the path. Note that the string may only + * contain ASCII characters in the ISO-8859-1 character set. If the filesystem exists, + * any metadata not included in the list will be removed. All metadata are removed + * if the header is omitted. To merge new and existing metadata, first get all + * existing metadata and the current E-Tag, then make a conditional request with the + * E-Tag and include values for all metadata. + */ + std::map Metadata; + + /** + * @brief Only valid if Hierarchical Namespace is enabled for the account. When creating + * a file or directory and the parent folder does not have a default ACL, the umask + * restricts the permissions of the file or directory to be created. The resulting + * permission is given by p bitwise and not u, where p is the permission and u is + * the umask. For example, if p is 0777 and u is 0057, then the resulting permission + * is 0720. The default permission is 0777 for a directory and 0666 for a file. + * The default umask is 0027. The umask must be specified in 4-digit octal + * notation (e.g. 0766). + */ + std::string Umask; + + /** + * @brief only valid if Hierarchical Namespace is enabled for the account. Sets POSIX + * access permissions for the file owner, the file owning group, and others. + * Each class may be granted read, write, or execute permission. + * The sticky bit is also supported. Both symbolic (rwxrw-rw-) and 4-digit octal + * notation (e.g. 0766) are supported. + */ + std::string Permissions; + }; + + /** + * @brief Optional parameters for DataLakePathClient::Create + * @remark Some optional parameter is mandatory in certain combination. + * More details: + * https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/create + */ + struct PathRenameOptions : public SharedRequestOptions + { + /** + * @brief Required only for Create File and Create Directory. The value must be + * PathResourceType::File or PathResourceType::Directory. + */ + PathResourceType Resource = PathResourceType::Unknown; + + /** + * @brief When renaming a directory, the number of paths that are renamed with each + * invocation is limited. If the number of paths to be renamed exceeds this limit, + * a continuation token is returned in this response header. When a continuation token + * is returned in the response, it must be specified in a subsequent invocation of the + * rename operation to continue renaming the directory. + */ + std::string Continuation; + + /** + * @brief Valid only when namespace is enabled. This parameter determines the behavior of the + * rename operation. The value must be PathRenameMode::Legacy or PathRenameMode::Posix, + * and the default value will be PathRenameMode::Posix. + */ + PathRenameMode Mode = PathRenameMode::Posix; + + /** + * @brief Sets the path's cache control. If specified, this property is stored with the + * path and returned with a read request. + */ + std::string CacheControl; + + /** + * @brief Sets the path's content type. If specified, this property is stored + * with the path and returned with a read request. + */ + std::string ContentType; + + /** + * @brief Sets the path's Content-Disposition header. + */ + std::string ContentDisposition; + + /** + * @brief Sets the path's content encoding. If specified, this property is stored with + * the path and returned with a read request. + */ + std::string ContentEncoding; + + /** + * @brief Set the path's content language. If specified, this property is stored with + * the path and returned with a read request. + */ + std::string ContentLanguage; + + /** + * @brief If specified, the operation only succeeds if the resource's lease is active and + * matches this ID. + */ + std::string LeaseId; + + /** + * @brief An optional file or directory to be renamed. The value must have the following + * format: "/{filesystem}/{path}". If Properties is specified, the properties + * will overwrite the existing properties; otherwise, the existing properties will + * be preserved. This value must be a URL percent-encoded string. Note that the string + * may only contain ASCII characters in the ISO-8859-1 character set. + */ + std::string RenameSource; + + /** + * @brief A lease ID for the source path. If specified, the source path must have an active + * lease and the leaase ID must match. + */ + std::string SourceLeaseId; + + /** + * @brief Specify an ETag value to operate only on path with a matching value. + */ + std::string IfMatch; + + /** + * @brief Specify an ETag value to operate only on path without a matching value. + */ + std::string IfNoneMatch; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has been modified since the + ( specified date and time. + */ + std::string IfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has not been modified since + * the specified date and time. + */ + std::string IfUnmodifiedSince; + + /** + * @brief User-defined metadata to be stored with the path. Note that the string may only + * contain ASCII characters in the ISO-8859-1 character set. If the filesystem exists, + * any metadata not included in the list will be removed. All metadata are removed + * if the header is omitted. To merge new and existing metadata, first get all + * existing metadata and the current E-Tag, then make a conditional request with the + * E-Tag and include values for all metadata. + */ + std::map Metadata; + + /** + * @brief Only valid if Hierarchical Namespace is enabled for the account. When creating + * a file or directory and the parent folder does not have a default ACL, the umask + * restricts the permissions of the file or directory to be created. The resulting + * permission is given by p bitwise and not u, where p is the permission and u is + * the umask. For example, if p is 0777 and u is 0057, then the resulting permission + * is 0720. The default permission is 0777 for a directory and 0666 for a file. + * The default umask is 0027. The umask must be specified in 4-digit octal + * notation (e.g. 0766). + */ + std::string Umask; + + /** + * @brief only valid if Hierarchical Namespace is enabled for the account. Sets POSIX + * access permissions for the file owner, the file owning group, and others. + * Each class may be granted read, write, or execute permission. + * The sticky bit is also supported. Both symbolic (rwxrw-rw-) and 4-digit octal + * notation (e.g. 0766) are supported. + */ + std::string Permissions; + + /** + * @brief Specify an ETag value to operate only on source path with a matching value. + */ + std::string SourceIfMatch; + + /** + * @brief Specify an ETag value to operate only on source path without a matching value. + */ + std::string SourceIfNoneMatch; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the source resource has been modified since the + ( specified date and time. + */ + std::string SourceIfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the source resource has not been modified since + * the specified date and time. + */ + std::string SourceIfUnmodifiedSince; + }; + + /** + * @brief Optional parameters for DataLakePathClient::Delete + * @remark Some optional parameter is mandatory in certain combination. + * More details: + * https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/delete + */ + struct PathDeleteOptions : public SharedRequestOptions + { + /** + * @brief When deleting a directory, the number of paths that are deleted with each invocation + * is limited. If the number of paths to be deleted exceeds this limit, a continuation + * token is returned in this response header. When a continuation token is returned in + * the response, it must be specified in a subsequent invocation of the delete operation + * to continue deleting the directory. + */ + std::string Continuation; + + /** + * @brief Required and valid only when the resource is a directory. If "true", all paths beneath + * the directory will be deleted. If "false" and the directory is non-empty, an error + * occurs. + */ + bool RecursiveOptional = bool(); + + /** + * @brief If specified, the operation only succeeds if the resource's lease is active and + * matches this ID. + */ + std::string LeaseId; + + /** + * @brief Specify an ETag value to operate only on path with a matching value. + */ + std::string IfMatch; + + /** + * @brief Specify an ETag value to operate only on path without a matching value. + */ + std::string IfNoneMatch; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has been modified since the + ( specified date and time. + */ + std::string IfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has not been modified since + * the specified date and time. + */ + std::string IfUnmodifiedSince; + }; + + /** + * @brief Optional parameters for DataLakePathClient::GetProperties + * @remark Some optional parameter is mandatory in certain combination. + * More details: + * https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/getproperties + */ + struct PathGetPropertiesOptions : public SharedRequestOptions + { + /** + * @brief If the value is PathGetPropertiesAction::GetStatus only the system defined properties + * for the path are returned. If the value is PathGetPropertiesAction::GetAccessControl + * the access control list is returned in the response headers (Hierarchical Namespace + * must be enabled for the account), otherwise the properties are returned. + */ + PathGetPropertiesAction Action = PathGetPropertiesAction::Unknown; + + /** + * @brief Valid only when Hierarchical Namespace is enabled for the account. If "true", + * the user identity values returned in the x-ms-owner, x-ms-group, and x-ms-acl + * response headers will be transformed from Azure Active Directory Object IDs to + * User Principal Names. If "false", the values will be returned as Azure Active + * Directory Object IDs. The default value is false. Note that group and application + * Object IDs are not translated because they do not have unique friendly names. + */ + bool UserPrincipalName = bool(); + + /** + * @brief If specified, the operation only succeeds if the resource's lease is active and + * matches this ID. + */ + std::string LeaseId; + + /** + * @brief Specify an ETag value to operate only on path with a matching value. + */ + std::string IfMatch; + + /** + * @brief Specify an ETag value to operate only on path without a matching value. + */ + std::string IfNoneMatch; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has been modified since the + ( specified date and time. + */ + std::string IfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has not been modified since + * the specified date and time. + */ + std::string IfUnmodifiedSince; + }; + + /** + * @brief Optional parameters for DataLakePathClient::Lease + * @remark Some optional parameter is mandatory in certain combination. + * More details: + * https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/lease + */ + struct PathLeaseOptions : public SharedRequestOptions + { + /** + * @brief There are five lease actions: PathLeaseAction::Acquire, PathLeaseAction::Break, + * PathLeaseAction::Change, PathLeaseAction::Renew, and PathLeaseAction::Release. + * Use PathLeaseAction::Acquire and specify the ProposedLeaseId and LeaseDuration + * to acquire a new lease. Use PathLeaseAction::Break to break an existing lease. + * When a lease is broken, the lease break period is allowed to elapse, during + * which time no lease operation except break and release can be performed on the file. + * When a lease is successfully broken, the response indicates the interval in seconds + * until a new lease can be acquired. Use PathLeaseAction::Change and specify the current + * lease ID in LeaseId and the new lease ID in ProposedLeaseId to change the lease ID of + * an active lease. Use PathLeaseAction::Renew and specify the LeaseId to renew an + * existing lease. Use PathLeaseAction::Release and specify the LeaseId to release a + * lease. + */ + PathLeaseAction LeaseAction; + + /** + * @brief Proposed lease ID, in a GUID string format. The DataLake service returns 400 + * (Invalid request) if the proposed lease ID is not in the correct format. + * See Guid Constructor (String) for a list of valid GUID string formats. + */ + std::string ProposedLeaseId; + + /** + * @brief The lease duration is required to acquire a lease, and specifies the duration + * of the lease in seconds. The lease duration must be between 15 and 60 seconds + * or -1 for infinite lease. + */ + int32_t LeaseDuration = int32_t(); + + /** + * @brief The lease break period duration is optional to break a lease, and specifies the + * break period of the lease in seconds. The lease break duration must be between + * 0 and 60 seconds. + */ + int32_t LeaseBreakPeriod = int32_t(); + + /** + * @brief If specified, the operation only succeeds if the resource's lease is active and + * matches this ID. + */ + std::string LeaseId; + + /** + * @brief Specify an ETag value to operate only on path with a matching value. + */ + std::string IfMatch; + + /** + * @brief Specify an ETag value to operate only on path without a matching value. + */ + std::string IfNoneMatch; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has been modified since the + ( specified date and time. + */ + std::string IfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has not been modified since + * the specified date and time. + */ + std::string IfUnmodifiedSince; + }; + + /** + * @brief Optional parameters for DataLakePathClient::Read + * @remark Some optional parameter is mandatory in certain combination. + * More details: + * https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/read + */ + struct PathReadOptions : public SharedRequestOptions + { + /** + * @brief The HTTP Range request header specifies one or more byte ranges of the resource + * to be retrieved. + */ + std::string Range; + + /** + * @brief When this header is set to "true" and specified together with the Range header, + * the service returns the MD5 hash for the range, as long as the range is less than + * or equal to 4MB in size. If this header is specified without the Range header, + * the service returns status code 400 (Bad Request). If this header is set to true + * when the range exceeds 4 MB in size, the service returns status code 400 (Bad + * Request). + */ + bool RangeGetContentMd5 = bool(); + + /** + * @brief If specified, the operation only succeeds if the resource's lease is active and + * matches this ID. + */ + std::string LeaseId; + + /** + * @brief Specify an ETag value to operate only on path with a matching value. + */ + std::string IfMatch; + + /** + * @brief Specify an ETag value to operate only on path without a matching value. + */ + std::string IfNoneMatch; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has been modified since the + ( specified date and time. + */ + std::string IfModifiedSince; + + /** + * @brief A date and time value. Specify this header to perform the + * operation only if the resource has not been modified since + * the specified date and time. + */ + std::string IfUnmodifiedSince; + }; +}}} // namespace Azure::Storage::DataLake diff --git a/sdk/storage/inc/datalake/datalake_utilities.hpp b/sdk/storage/inc/datalake/datalake_utilities.hpp new file mode 100644 index 000000000..2e649eeb1 --- /dev/null +++ b/sdk/storage/inc/datalake/datalake_utilities.hpp @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common/storage_url_builder.hpp" + +#include +#include + +namespace Azure { namespace Storage { namespace DataLake { + + namespace Details { + UrlBuilder GetBlobUriFromDfsUri(const UrlBuilder& dfsUri); + } // namespace Details + + std::map DeserializeMetadata( + const std::string& dataLakePropertiesString); + + std::string SerializeMetadata(const std::map& dataLakePropertiesMap); + +}}} // namespace Azure::Storage::DataLake diff --git a/sdk/storage/inc/datalake/file_system_client.hpp b/sdk/storage/inc/datalake/file_system_client.hpp new file mode 100644 index 000000000..4bafe6f2f --- /dev/null +++ b/sdk/storage/inc/datalake/file_system_client.hpp @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common/storage_credential.hpp" +#include "common/storage_url_builder.hpp" +#include "datalake/service_client.hpp" +#include "datalake_options.hpp" +#include "http/pipeline.hpp" +#include "protocol/datalake_rest_client.hpp" + +#include +#include + +namespace Azure { namespace Storage { namespace DataLake { + + class PathClient; + + struct FileSystemGetMetadataResponse + { + std::string Date; + std::string ETag; + std::string LastModified; + std::string RequestId; + std::string Version; + std::map Metadata; + bool NamespaceEnabled; + }; + + typedef FileSystemSetPropertiesResponse FileSystemSetMetadataResponse; + + class FileSystemClient { + public: + /** + * @brief Create from connection string + * @param connectionString Azure Storage connection string. + * @param fileSystemName The name of a file system. + * @param options Optional parameters used to initialize the client. + * @return FileSystemClient + */ + static FileSystemClient CreateFromConnectionString( + const std::string& connectionString, + const std::string& fileSystemName, + const FileSystemClientOptions& options = FileSystemClientOptions()); + + /** + * @brief Shared key authentication client. + * @param fileSystemUri The URI of the file system this client's request targets. + * @param credential The shared key credential used to initialize the client. + * @param options Optional parameters used to initialize the client. + */ + explicit FileSystemClient( + const std::string& fileSystemUri, + std::shared_ptr credential, + const FileSystemClientOptions& options = FileSystemClientOptions()); + + /** + * @brief Bearer token authentication client. + * @param fileSystemUri The URI of the file system this client's request targets. + * @param credential The token credential used to initialize the client. + * @param options Optional parameters used to initialize the client. + */ + explicit FileSystemClient( + const std::string& fileSystemUri, + std::shared_ptr credential, + const FileSystemClientOptions& options = FileSystemClientOptions()); + + /** + * @brief Anonymous/SAS/customized pipeline auth. + * @param fileSystemUri The URI of the file system this client's request targets. + * @param options Optional parameters used to initialize the client. + */ + explicit FileSystemClient( + const std::string& fileSystemUri, + const FileSystemClientOptions& options = FileSystemClientOptions()); + + /** + * @brief Create a PathClient from current FileSystemClient + * @param path Path of the resource within the file system. + * @return PathClient + */ + PathClient GetPathClient(const std::string& path) const; + + /** + * @brief Creates the file system. + * @param options Optional parameters to create this file system. + * @return FileSystemCreateResponse + */ + FileSystemCreateResponse Create( + const FileSystemCreateOptions& options = FileSystemCreateOptions()) const; + + /** + * @brief Deletes the file system. + * @param options Optional parameters to delete this file system. + * @return FileSystemDeleteResponse + */ + FileSystemDeleteResponse Delete( + const FileSystemDeleteOptions& options = FileSystemDeleteOptions()) const; + + /** + * @brief Gets the metadata of file system. + * @param options Optional parameters to get the metadata of this file system. + * @return FileSystemGetMetadataResponse + */ + FileSystemGetMetadataResponse GetMetadata( + const FileSystemGetMetadataOptions& options = FileSystemGetMetadataOptions()) const; + + /** + * @brief Sets the metadata of file system. + * @param metadata User-defined metadata to be stored with the filesystem. Note that the string + * may only contain ASCII characters in the ISO-8859-1 character set. + * @param options Optional parameters to set the metadata to this file system. + * @return FileSystemSetMetadataResponse + */ + FileSystemSetMetadataResponse SetMetadata( + const std::map& metadata, + const FileSystemSetMetadataOptions& options = FileSystemSetMetadataOptions()) const; + + /** + * @brief List the paths in this file system. + * @param recursive If "true", all paths are listed; otherwise, only paths at the root of the + * filesystem are listed. If "directory" is specified, the list will only + * include paths that share the same root. + * @param options Optional parameters to list the paths in file system. + * @return FileSystemListPathsResponse + */ + FileSystemListPathsResponse ListPaths( + bool recursive, + const ListPathsOptions& options = ListPathsOptions()) const; + + private: + UrlBuilder m_dfsUri; + UrlBuilder m_blobUri; + std::shared_ptr m_pipeline; + + FileSystemClient() = default; + friend class ServiceClient; + }; +}}} // namespace Azure::Storage::DataLake diff --git a/sdk/storage/inc/datalake/path_client.hpp b/sdk/storage/inc/datalake/path_client.hpp new file mode 100644 index 000000000..ba995df9c --- /dev/null +++ b/sdk/storage/inc/datalake/path_client.hpp @@ -0,0 +1,249 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common/storage_credential.hpp" +#include "common/storage_url_builder.hpp" +#include "datalake/file_system_client.hpp" +#include "datalake_options.hpp" +#include "http/pipeline.hpp" +#include "protocol/datalake_rest_client.hpp" + +#include +#include + +namespace Azure { namespace Storage { namespace DataLake { + + struct ReadPathResponse + { + Azure::Core::Http::BodyStream* Body; + std::string AcceptRanges; + std::string CacheControl; + std::string ContentDisposition; + std::string ContentEncoding; + std::string ContentLanguage; + int64_t ContentLength = int64_t(); + std::string ContentRange; + std::string ContentType; + std::string ContentMD5; + std::string Date; + std::string ETag; + std::string LastModified; + std::string RequestId; + std::string Version; + std::string ResourceType; + std::string LeaseDuration; + std::string LeaseState; + std::string LeaseStatus; + std::string ContentMd5; + std::map Metadata; + }; + + struct GetPathPropertiesResponse + { + std::string AcceptRanges; + std::string CacheControl; + std::string ContentDisposition; + std::string ContentEncoding; + std::string ContentLanguage; + int64_t ContentLength = int64_t(); + std::string ContentRange; + std::string ContentType; + std::string ContentMD5; + std::string Date; + std::string ETag; + std::string LastModified; + std::string RequestId; + std::string Version; + std::string ResourceType; + std::string Owner; + std::string Group; + std::string Permissions; + std::string ACL; + std::string LeaseDuration; + std::string LeaseState; + std::string LeaseStatus; + std::map Metadata; + }; + + typedef PathCreateResponse PathRenameResponse; + + class PathClient { + public: + /** + * @brief Create from connection string + * @param connectionString Azure Storage connection string. + * @param fileSystemName The name of a file system. + * @param path The path of a resource within the file system. + * @param options Optional parameters used to initialize the client. + * @return PathClient + */ + static PathClient CreateFromConnectionString( + const std::string& connectionString, + const std::string& fileSystemName, + const std::string& path, + const PathClientOptions& options = PathClientOptions()); + + /** + * @brief Shared key authentication client. + * @param pathUri The URI of the file system this client's request targets. + * @param credential The shared key credential used to initialize the client. + * @param options Optional parameters used to initialize the client. + */ + explicit PathClient( + const std::string& pathUri, + std::shared_ptr credential, + const PathClientOptions& options = PathClientOptions()); + + /** + * @brief Bearer token authentication client. + * @param pathUri The URI of the file system this client's request targets. + * @param credential The token credential used to initialize the client. + * @param options Optional parameters used to initialize the client. + */ + explicit PathClient( + const std::string& pathUri, + std::shared_ptr credential, + const PathClientOptions& options = PathClientOptions()); + + /** + * @brief Anonymous/SAS/customized pipeline auth. + * @param pathUri The URI of the file system this client's request targets. + * @param options Optional parameters used to initialize the client. + */ + explicit PathClient( + const std::string& pathUri, + const PathClientOptions& options = PathClientOptions()); + + /** + * @brief Uploads data to be appended to a file. Data can only be appended to a file. + * @param data The data to be appended. + * @param offset This parameter allows the caller to upload data in parallel and control + * the order in which it is appended to the file. + * The value must be the offset where the data is to be appended. + * Uploaded data is not immediately flushed, or written, to the file. To flush, + * the previously uploaded data must be contiguous, the offset parameter must + * be specified and equal to the length of the file after all data has been + * written, and there must not be a request entity body included with the + * request. + * @param options Optional parameters to append data to the resource the path points to. + * @return PathAppendDataResponse + */ + PathAppendDataResponse AppendData( + Azure::Core::Http::BodyStream* stream, + int64_t offset, + const PathAppendDataOptions& options = PathAppendDataOptions()) const; + + /** + * @brief Flushes previous uploaded data to a file. + * @param offset This parameter allows the caller to upload data in parallel and control + * the order in which it is appended to the file. + * The value must be the offset where the data is to be appended. + * Uploaded data is not immediately flushed, or written, to the file. To flush, + * the previously uploaded data must be contiguous, the offset parameter must + * be specified and equal to the length of the file after all data has been + * written, and there must not be a request entity body included with the + * request. + * @param options Optional parameters to flush data to the resource the path points to. + * @return PathFlushDataResponse + */ + PathFlushDataResponse FlushData( + int64_t offset, + const PathFlushDataOptions& options = PathFlushDataOptions()) const; + + /** + * @brief Sets the owner, group, permissions, or access control list for a file or directory. + * Note that Hierarchical Namespace must be enabled for the account in order to use + * access control. Also note that the Access Control List (ACL) includes permissions for + * the owner, owning group, and others, so the x-ms-permissions and x-ms-acl request + * headers are mutually exclusive. + * @param options Optional parameters to set an access control to the resource the path points + * to. + * @return PathFlushDataResponse + */ + PathSetAccessControlResponse SetAccessControl( + const SetAccessControlOptions& options = SetAccessControlOptions()) const; + + /** + * @brief Sets POSIX access control rights on files and directories under given path + * recursively. + * @param mode Mode PathSetAccessControlRecursiveMode::Set sets POSIX access control rights on + * files and directories, PathSetAccessControlRecursiveMode::Modify modifies one or + * more POSIX access control rights that pre-exist on files and directories, + * PathSetAccessControlRecursiveMode::Remove removes one or more POSIX access + * control rights that were present earlier on files and directories + * @param options Optional parameters to set an access control recursively to the resource the + * path points to. + * @return PathSetAccessControlRecursiveResponse + */ + PathSetAccessControlRecursiveResponse SetAccessControlRecursive( + PathSetAccessControlRecursiveMode mode, + const SetAccessControlRecursiveOptions& options = SetAccessControlRecursiveOptions()) const; + + /** + * @brief Sets the properties of a resource the path points to. + * @param options Optional parameters to set the properties to the resource the path points to. + * @return PathUpdateResponse + */ + PathUpdateResponse SetProperties( + const SetPathPropertiesOptions& options = SetPathPropertiesOptions()) const; + + /** + * @brief Creates a file or directory. By default, the destination is overwritten and + * if the destination already exists and has a lease the lease is broken. + * @param options Optional parameters to create the resource the path points to. + * @return PathCreateResponse + */ + PathCreateResponse Create(const PathCreateOptions& options = PathCreateOptions()) const; + + /** + * @brief Renames a file or directory. By default, the destination is overwritten and + * if the destination already exists and has a lease the lease is broken. + * @param options Optional parameters to rename a resource to the resource the path points to. + * @return PathRenameResponse + */ + PathRenameResponse Rename(const PathRenameOptions& options = PathRenameOptions()) const; + + /** + * @brief Deletes the file or directory. + * @param options Optional parameters to delete the resource the path points to. + * @return PathDeleteResponse + */ + PathDeleteResponse Delete(const PathDeleteOptions& options = PathDeleteOptions()) const; + + /** + * @brief Get Properties returns all system and user defined properties for a path. Get Status + * returns all system defined properties for a path. Get Access Control List returns the + * access control list for a path. + * @param options Optional parameters to get the properties from the resource the path points + * to. + * @return GetPathPropertiesResponse + */ + GetPathPropertiesResponse GetProperties( + const PathGetPropertiesOptions& options = PathGetPropertiesOptions()) const; + + /** + * @brief Create and manage a lease to restrict write and delete access to the path. + * @param options Optional parameters to create or manage a lease on the resource the path + * points to. + * @return PathLeaseResponse + */ + PathLeaseResponse Lease(const PathLeaseOptions& options = PathLeaseOptions()) const; + + /** + * @brief Read the contents of a file. For read operations, range requests are supported. + * @param options Optional parameters to read the content from the resource the path points to. + * @return ReadPathResponse + */ + ReadPathResponse Read(const PathReadOptions& options = PathReadOptions()) const; + + private: + UrlBuilder m_dfsUri; + UrlBuilder m_blobUri; + std::shared_ptr m_pipeline; + + PathClient() = default; + friend class FileSystemClient; + }; +}}} // namespace Azure::Storage::DataLake diff --git a/sdk/storage/inc/datalake/protocol/datalake_rest_client.hpp b/sdk/storage/inc/datalake/protocol/datalake_rest_client.hpp index f4b128618..35ef245e7 100644 --- a/sdk/storage/inc/datalake/protocol/datalake_rest_client.hpp +++ b/sdk/storage/inc/datalake/protocol/datalake_rest_client.hpp @@ -4,9 +4,10 @@ #pragma once -#include "external/json.hpp" -#include "http/curl/curl.hpp" +#include "common/storage_error.hpp" #include "http/http.hpp" +#include "http/pipeline.hpp" +#include "json.hpp" #include #include @@ -17,86 +18,90 @@ namespace Azure { namespace Storage { namespace DataLake { - constexpr static const char* k_DefaultServiceApiVersion = "2019-12-12"; - constexpr static const char* k_PathDnsSuffixDefault = "dfs.core.windows.net"; - constexpr static const char* k_QueryFileSystemResource = "resource"; - constexpr static const char* k_QueryTimeout = "timeout"; - constexpr static const char* k_QueryRecursiveOptional = "recursive"; - constexpr static const char* k_QueryRecursiveRequired = "recursive"; - constexpr static const char* k_QueryContinuation = "continuation"; - constexpr static const char* k_QueryPathSetAccessControlRecursiveMode = "mode"; - constexpr static const char* k_QueryDirectory = "directory"; - constexpr static const char* k_QueryPrefix = "prefix"; - constexpr static const char* k_QueryMaxResults = "maxResults"; - constexpr static const char* k_QueryUpn = "upn"; - constexpr static const char* k_QueryPosition = "position"; - constexpr static const char* k_QueryRetainUncommittedData = "retainUncommittedData"; - constexpr static const char* k_QueryClose = "close"; - constexpr static const char* k_QueryResource = "resource"; - constexpr static const char* k_QueryPathResourceType = "resource"; - constexpr static const char* k_QueryPathRenameMode = "mode"; - constexpr static const char* k_QueryPathUpdateAction = "action"; - constexpr static const char* k_QueryMaxRecords = "maxRecords"; - constexpr static const char* k_QueryPathGetPropertiesAction = "action"; - constexpr static const char* k_QueryAction = "action"; - constexpr static const char* k_HeaderApiVersionParameter = "x-ms-version"; - constexpr static const char* k_HeaderClientRequestId = "x-ms-client-request-id"; - constexpr static const char* k_HeaderIfMatch = "If-Match"; - constexpr static const char* k_HeaderIfModifiedSince = "If-Modified-Since"; - constexpr static const char* k_HeaderIfNoneMatch = "If-None-Match"; - constexpr static const char* k_HeaderIfUnmodifiedSince = "If-Unmodified-Since"; - constexpr static const char* k_HeaderLeaseIdOptional = "x-ms-lease-id"; - constexpr static const char* k_HeaderLeaseIdRequired = "x-ms-lease-id"; - constexpr static const char* k_HeaderProposedLeaseIdOptional = "x-ms-proposed-lease-id"; - constexpr static const char* k_HeaderProperties = "x-ms-properties"; - constexpr static const char* k_HeaderSourceIfMatch = "x-ms-source-if-match"; - constexpr static const char* k_HeaderSourceIfModifiedSince = "x-ms-source-if-modified-since"; - constexpr static const char* k_HeaderSourceIfNoneMatch = "x-ms-source-if-none-match"; - constexpr static const char* k_HeaderSourceIfUnmodifiedSince = "x-ms-source-if-unmodified-since"; - constexpr static const char* k_HeaderSourceLeaseId = "x-ms-source-lease-id"; - constexpr static const char* k_HeaderCacheControl = "x-ms-cache-control"; - constexpr static const char* k_HeaderContentDisposition = "x-ms-content-disposition"; - constexpr static const char* k_HeaderContentEncoding = "x-ms-content-encoding"; - constexpr static const char* k_HeaderContentLanguage = "x-ms-content-language"; - constexpr static const char* k_HeaderContentType = "x-ms-content-type"; - constexpr static const char* k_HeaderTransactionalContentMD5 = "Content-MD5"; - constexpr static const char* k_HeaderContentMD5 = "x-ms-content-md5"; - constexpr static const char* k_HeaderUmask = "x-ms-umask"; - constexpr static const char* k_HeaderPermissions = "x-ms-permissions"; - constexpr static const char* k_HeaderRenameSource = "x-ms-rename-source"; - constexpr static const char* k_HeaderOwner = "x-ms-owner"; - constexpr static const char* k_HeaderGroup = "x-ms-group"; - constexpr static const char* k_HeaderAcl = "x-ms-acl"; - constexpr static const char* k_HeaderContentLength = "Content-Length"; - constexpr static const char* k_HeaderDate = "Date"; - constexpr static const char* k_HeaderXMsRequestId = "x-ms-request-id"; - constexpr static const char* k_HeaderXMsVersion = "x-ms-version"; - constexpr static const char* k_HeaderXMsContinuation = "x-ms-continuation"; - constexpr static const char* k_HeaderXMsErrorCode = "x-ms-error-code"; - constexpr static const char* k_HeaderETag = "ETag"; - constexpr static const char* k_HeaderLastModified = "Last-Modified"; - constexpr static const char* k_HeaderXMsNamespaceEnabled = "x-ms-namespace-enabled"; - constexpr static const char* k_HeaderXMsProperties = "x-ms-properties"; - constexpr static const char* k_HeaderAcceptRanges = "Accept-Ranges"; - constexpr static const char* k_HeaderContentRange = "Content-Range"; - constexpr static const char* k_HeaderPathLeaseAction = "x-ms-lease-action"; - constexpr static const char* k_HeaderXMsLeaseDuration = "x-ms-lease-duration"; - constexpr static const char* k_HeaderXMsLeaseBreakPeriod = "x-ms-lease-break-period"; - constexpr static const char* k_HeaderXMsLeaseId = "x-ms-lease-id"; - constexpr static const char* k_HeaderXMsLeaseTime = "x-ms-lease-time"; - constexpr static const char* k_HeaderRange = "Range"; - constexpr static const char* k_HeaderXMsRangeGetContentMd5 = "x-ms-range-get-content-md5"; - constexpr static const char* k_HeaderXMsResourceType = "x-ms-resource-type"; - constexpr static const char* k_HeaderXMsLeaseState = "x-ms-lease-state"; - constexpr static const char* k_HeaderXMsLeaseStatus = "x-ms-lease-status"; - constexpr static const char* k_HeaderXMsContentMd5 = "x-ms-content-md5"; - constexpr static const char* k_HeaderXMsOwner = "x-ms-owner"; - constexpr static const char* k_HeaderXMsGroup = "x-ms-group"; - constexpr static const char* k_HeaderXMsPermissions = "x-ms-permissions"; - constexpr static const char* k_HeaderXMsAcl = "x-ms-acl"; - constexpr static const char* k_HeaderXMsClientRequestId = "x-ms-client-request-id"; - - // Mode "set" sets POSIX access control rights on files and directories, "modify" modifies one or more POSIX access control rights that pre-exist on files and directories, "remove" removes one or more POSIX access control rights that were present earlier on files and directories + namespace Details { + constexpr static const char* c_DefaultServiceApiVersion = ""; + constexpr static const char* c_PathDnsSuffixDefault = "dfs.core.windows.net"; + constexpr static const char* c_QueryFileSystemResource = "resource"; + constexpr static const char* c_QueryTimeout = "timeout"; + constexpr static const char* c_QueryRecursiveOptional = "recursive"; + constexpr static const char* c_QueryRecursiveRequired = "recursive"; + constexpr static const char* c_QueryContinuation = "continuation"; + constexpr static const char* c_QueryPathSetAccessControlRecursiveMode = "mode"; + constexpr static const char* c_QueryDirectory = "directory"; + constexpr static const char* c_QueryPrefix = "prefix"; + constexpr static const char* c_QueryMaxResults = "maxResults"; + constexpr static const char* c_QueryUpn = "upn"; + constexpr static const char* c_QueryPosition = "position"; + constexpr static const char* c_QueryRetainUncommittedData = "retainUncommittedData"; + constexpr static const char* c_QueryClose = "close"; + constexpr static const char* c_QueryResource = "resource"; + constexpr static const char* c_QueryPathResourceType = "resource"; + constexpr static const char* c_QueryPathRenameMode = "mode"; + constexpr static const char* c_QueryPathUpdateAction = "action"; + constexpr static const char* c_QueryMaxRecords = "maxRecords"; + constexpr static const char* c_QueryPathGetPropertiesAction = "action"; + constexpr static const char* c_QueryAction = "action"; + constexpr static const char* c_HeaderApiVersionParameter = "x-ms-version"; + constexpr static const char* c_HeaderClientRequestId = "x-ms-client-request-id"; + constexpr static const char* c_HeaderIfMatch = "If-Match"; + constexpr static const char* c_HeaderIfModifiedSince = "If-Modified-Since"; + constexpr static const char* c_HeaderIfNoneMatch = "If-None-Match"; + constexpr static const char* c_HeaderIfUnmodifiedSince = "If-Unmodified-Since"; + constexpr static const char* c_HeaderLeaseIdOptional = "x-ms-lease-id"; + constexpr static const char* c_HeaderLeaseIdRequired = "x-ms-lease-id"; + constexpr static const char* c_HeaderProposedLeaseIdOptional = "x-ms-proposed-lease-id"; + constexpr static const char* c_HeaderProperties = "x-ms-properties"; + constexpr static const char* c_HeaderSourceIfMatch = "x-ms-source-if-match"; + constexpr static const char* c_HeaderSourceIfModifiedSince = "x-ms-source-if-modified-since"; + constexpr static const char* c_HeaderSourceIfNoneMatch = "x-ms-source-if-none-match"; + constexpr static const char* c_HeaderSourceIfUnmodifiedSince + = "x-ms-source-if-unmodified-since"; + constexpr static const char* c_HeaderSourceLeaseId = "x-ms-source-lease-id"; + constexpr static const char* c_HeaderCacheControl = "x-ms-cache-control"; + constexpr static const char* c_HeaderContentDisposition = "x-ms-content-disposition"; + constexpr static const char* c_HeaderContentEncoding = "x-ms-content-encoding"; + constexpr static const char* c_HeaderContentLanguage = "x-ms-content-language"; + constexpr static const char* c_HeaderContentType = "x-ms-content-type"; + constexpr static const char* c_HeaderTransactionalContentMD5 = "Content-MD5"; + constexpr static const char* c_HeaderContentMD5 = "x-ms-content-md5"; + constexpr static const char* c_HeaderUmask = "x-ms-umask"; + constexpr static const char* c_HeaderPermissions = "x-ms-permissions"; + constexpr static const char* c_HeaderRenameSource = "x-ms-rename-source"; + constexpr static const char* c_HeaderOwner = "x-ms-owner"; + constexpr static const char* c_HeaderGroup = "x-ms-group"; + constexpr static const char* c_HeaderAcl = "x-ms-acl"; + constexpr static const char* c_HeaderContentLength = "Content-Length"; + constexpr static const char* c_HeaderDate = "Date"; + constexpr static const char* c_HeaderXMsRequestId = "x-ms-request-id"; + constexpr static const char* c_HeaderXMsVersion = "x-ms-version"; + constexpr static const char* c_HeaderXMsContinuation = "x-ms-continuation"; + constexpr static const char* c_HeaderXMsErrorCode = "x-ms-error-code"; + constexpr static const char* c_HeaderETag = "ETag"; + constexpr static const char* c_HeaderLastModified = "Last-Modified"; + constexpr static const char* c_HeaderXMsNamespaceEnabled = "x-ms-namespace-enabled"; + constexpr static const char* c_HeaderXMsProperties = "x-ms-properties"; + constexpr static const char* c_HeaderAcceptRanges = "Accept-Ranges"; + constexpr static const char* c_HeaderContentRange = "Content-Range"; + constexpr static const char* c_HeaderPathLeaseAction = "x-ms-lease-action"; + constexpr static const char* c_HeaderXMsLeaseDuration = "x-ms-lease-duration"; + constexpr static const char* c_HeaderXMsLeaseBreakPeriod = "x-ms-lease-break-period"; + constexpr static const char* c_HeaderXMsLeaseId = "x-ms-lease-id"; + constexpr static const char* c_HeaderXMsLeaseTime = "x-ms-lease-time"; + constexpr static const char* c_HeaderRange = "Range"; + constexpr static const char* c_HeaderXMsRangeGetContentMd5 = "x-ms-range-get-content-md5"; + constexpr static const char* c_HeaderXMsResourceType = "x-ms-resource-type"; + constexpr static const char* c_HeaderXMsLeaseState = "x-ms-lease-state"; + constexpr static const char* c_HeaderXMsLeaseStatus = "x-ms-lease-status"; + constexpr static const char* c_HeaderXMsContentMd5 = "x-ms-content-md5"; + constexpr static const char* c_HeaderXMsOwner = "x-ms-owner"; + constexpr static const char* c_HeaderXMsGroup = "x-ms-group"; + constexpr static const char* c_HeaderXMsPermissions = "x-ms-permissions"; + constexpr static const char* c_HeaderXMsAcl = "x-ms-acl"; + constexpr static const char* c_HeaderXMsClientRequestId = "x-ms-client-request-id"; + } // namespace Details + // Mode "set" sets POSIX access control rights on files and directories, "modify" modifies one or + // more POSIX access control rights that pre-exist on files and directories, "remove" removes one + // or more POSIX access control rights that were present earlier on files and directories enum class PathSetAccessControlRecursiveMode { Set, @@ -105,7 +110,8 @@ namespace Azure { namespace Storage { namespace DataLake { Unknown }; - static std::string PathSetAccessControlRecursiveModeToString(const PathSetAccessControlRecursiveMode& pathSetAccessControlRecursiveMode) + inline std::string PathSetAccessControlRecursiveModeToString( + const PathSetAccessControlRecursiveMode& pathSetAccessControlRecursiveMode) { switch (pathSetAccessControlRecursiveMode) { @@ -120,7 +126,8 @@ namespace Azure { namespace Storage { namespace DataLake { } } - static PathSetAccessControlRecursiveMode PathSetAccessControlRecursiveModeFromString(const std::string& pathSetAccessControlRecursiveMode) + inline PathSetAccessControlRecursiveMode PathSetAccessControlRecursiveModeFromString( + const std::string& pathSetAccessControlRecursiveMode) { if (pathSetAccessControlRecursiveMode == "set") { @@ -134,8 +141,10 @@ namespace Azure { namespace Storage { namespace DataLake { { return PathSetAccessControlRecursiveMode::Remove; } - throw std::runtime_error("Cannot convert " + pathSetAccessControlRecursiveMode + " to PathSetAccessControlRecursiveMode"); - }; + throw std::runtime_error( + "Cannot convert " + pathSetAccessControlRecursiveMode + + " to PathSetAccessControlRecursiveMode"); + } struct AclFailedEntry { @@ -155,17 +164,17 @@ namespace Azure { namespace Storage { namespace DataLake { struct SetAccessControlRecursiveResponse { - uint32_t DirectoriesSuccessful; - uint32_t FilesSuccessful; - uint32_t FailureCount; + int32_t DirectoriesSuccessful = int32_t(); + int32_t FilesSuccessful = int32_t(); + int32_t FailureCount = int32_t(); std::vector FailedEntries; static SetAccessControlRecursiveResponse CreateFromJson(const nlohmann::json& node) { SetAccessControlRecursiveResponse result; - result.DirectoriesSuccessful = static_cast(std::stoul(node["directoriesSuccessful"].get())); - result.FilesSuccessful = static_cast(std::stoul(node["filesSuccessful"].get())); - result.FailureCount = static_cast(std::stoul(node["failureCount"].get())); + result.DirectoriesSuccessful = std::stoi(node["directoriesSuccessful"].get()); + result.FilesSuccessful = std::stoi(node["filesSuccessful"].get()); + result.FailureCount = std::stoi(node["failureCount"].get()); for (const auto& element : node["failedEntries"]) { result.FailedEntries.emplace_back(AclFailedEntry::CreateFromJson(element)); @@ -177,10 +186,10 @@ namespace Azure { namespace Storage { namespace DataLake { struct Path { std::string Name; - bool IsDirectory; + bool IsDirectory = bool(); std::string LastModified; std::string ETag; - uint64_t ContentLength; + int64_t ContentLength = int64_t(); std::string Owner; std::string Group; std::string Permissions; @@ -192,7 +201,7 @@ namespace Azure { namespace Storage { namespace DataLake { result.IsDirectory = (node["isDirectory"].get() == "true"); result.LastModified = node["lastModified"].get(); result.ETag = node["eTag"].get(); - result.ContentLength = std::stoull(node["contentLength"].get()); + result.ContentLength = std::stoll(node["contentLength"].get()); result.Owner = node["owner"].get(); result.Group = node["group"].get(); result.Permissions = node["permissions"].get(); @@ -267,7 +276,7 @@ namespace Azure { namespace Storage { namespace DataLake { Unknown }; - static std::string PathResourceTypeToString(const PathResourceType& pathResourceType) + inline std::string PathResourceTypeToString(const PathResourceType& pathResourceType) { switch (pathResourceType) { @@ -280,7 +289,7 @@ namespace Azure { namespace Storage { namespace DataLake { } } - static PathResourceType PathResourceTypeFromString(const std::string& pathResourceType) + inline PathResourceType PathResourceTypeFromString(const std::string& pathResourceType) { if (pathResourceType == "directory") { @@ -291,9 +300,10 @@ namespace Azure { namespace Storage { namespace DataLake { return PathResourceType::File; } throw std::runtime_error("Cannot convert " + pathResourceType + " to PathResourceType"); - }; + } - // Optional. Valid only when namespace is enabled. This parameter determines the behavior of the rename operation. The value must be "legacy" or "posix", and the default value will be "posix". + // Optional. Valid only when namespace is enabled. This parameter determines the behavior of the + // rename operation. The value must be "legacy" or "posix", and the default value will be "posix". enum class PathRenameMode { Legacy, @@ -301,7 +311,7 @@ namespace Azure { namespace Storage { namespace DataLake { Unknown }; - static std::string PathRenameModeToString(const PathRenameMode& pathRenameMode) + inline std::string PathRenameModeToString(const PathRenameMode& pathRenameMode) { switch (pathRenameMode) { @@ -314,7 +324,7 @@ namespace Azure { namespace Storage { namespace DataLake { } } - static PathRenameMode PathRenameModeFromString(const std::string& pathRenameMode) + inline PathRenameMode PathRenameModeFromString(const std::string& pathRenameMode) { if (pathRenameMode == "legacy") { @@ -325,9 +335,16 @@ namespace Azure { namespace Storage { namespace DataLake { return PathRenameMode::Posix; } throw std::runtime_error("Cannot convert " + pathRenameMode + " to PathRenameMode"); - }; + } - // The action must be "append" to upload data to be appended to a file, "flush" to flush previously uploaded data to a file, "setProperties" to set the properties of a file or directory, "setAccessControl" to set the owner, group, permissions, or access control list for a file or directory, or "setAccessControlRecursive" to set the access control list for a directory recursively. Note that Hierarchical Namespace must be enabled for the account in order to use access control. Also note that the Access Control List (ACL) includes permissions for the owner, owning group, and others, so the x-ms-permissions and x-ms-acl request headers are mutually exclusive. + // The action must be "append" to upload data to be appended to a file, "flush" to flush + // previously uploaded data to a file, "setProperties" to set the properties of a file or + // directory, "setAccessControl" to set the owner, group, permissions, or access control list for + // a file or directory, or "setAccessControlRecursive" to set the access control list for a + // directory recursively. Note that Hierarchical Namespace must be enabled for the account in + // order to use access control. Also note that the Access Control List (ACL) includes permissions + // for the owner, owning group, and others, so the x-ms-permissions and x-ms-acl request headers + // are mutually exclusive. enum class PathUpdateAction { Append, @@ -338,7 +355,7 @@ namespace Azure { namespace Storage { namespace DataLake { Unknown }; - static std::string PathUpdateActionToString(const PathUpdateAction& pathUpdateAction) + inline std::string PathUpdateActionToString(const PathUpdateAction& pathUpdateAction) { switch (pathUpdateAction) { @@ -357,7 +374,7 @@ namespace Azure { namespace Storage { namespace DataLake { } } - static PathUpdateAction PathUpdateActionFromString(const std::string& pathUpdateAction) + inline PathUpdateAction PathUpdateActionFromString(const std::string& pathUpdateAction) { if (pathUpdateAction == "append") { @@ -380,9 +397,17 @@ namespace Azure { namespace Storage { namespace DataLake { return PathUpdateAction::SetAccessControlRecursive; } throw std::runtime_error("Cannot convert " + pathUpdateAction + " to PathUpdateAction"); - }; + } - // There are five lease actions: "acquire", "break", "change", "renew", and "release". Use "acquire" and specify the "x-ms-proposed-lease-id" and "x-ms-lease-duration" to acquire a new lease. Use "break" to break an existing lease. When a lease is broken, the lease break period is allowed to elapse, during which time no lease operation except break and release can be performed on the file. When a lease is successfully broken, the response indicates the interval in seconds until a new lease can be acquired. Use "change" and specify the current lease ID in "x-ms-lease-id" and the new lease ID in "x-ms-proposed-lease-id" to change the lease ID of an active lease. Use "renew" and specify the "x-ms-lease-id" to renew an existing lease. Use "release" and specify the "x-ms-lease-id" to release a lease. + // There are five lease actions: "acquire", "break", "change", "renew", and "release". Use + // "acquire" and specify the "x-ms-proposed-lease-id" and "x-ms-lease-duration" to acquire a new + // lease. Use "break" to break an existing lease. When a lease is broken, the lease break period + // is allowed to elapse, during which time no lease operation except break and release can be + // performed on the file. When a lease is successfully broken, the response indicates the interval + // in seconds until a new lease can be acquired. Use "change" and specify the current lease ID in + // "x-ms-lease-id" and the new lease ID in "x-ms-proposed-lease-id" to change the lease ID of an + // active lease. Use "renew" and specify the "x-ms-lease-id" to renew an existing lease. Use + // "release" and specify the "x-ms-lease-id" to release a lease. enum class PathLeaseAction { Acquire, @@ -393,7 +418,7 @@ namespace Azure { namespace Storage { namespace DataLake { Unknown }; - static std::string PathLeaseActionToString(const PathLeaseAction& pathLeaseAction) + inline std::string PathLeaseActionToString(const PathLeaseAction& pathLeaseAction) { switch (pathLeaseAction) { @@ -412,7 +437,7 @@ namespace Azure { namespace Storage { namespace DataLake { } } - static PathLeaseAction PathLeaseActionFromString(const std::string& pathLeaseAction) + inline PathLeaseAction PathLeaseActionFromString(const std::string& pathLeaseAction) { if (pathLeaseAction == "acquire") { @@ -435,9 +460,12 @@ namespace Azure { namespace Storage { namespace DataLake { return PathLeaseAction::Release; } throw std::runtime_error("Cannot convert " + pathLeaseAction + " to PathLeaseAction"); - }; + } - // Optional. If the value is "getStatus" only the system defined properties for the path are returned. If the value is "getAccessControl" the access control list is returned in the response headers (Hierarchical Namespace must be enabled for the account), otherwise the properties are returned. + // Optional. If the value is "getStatus" only the system defined properties for the path are + // returned. If the value is "getAccessControl" the access control list is returned in the + // response headers (Hierarchical Namespace must be enabled for the account), otherwise the + // properties are returned. enum class PathGetPropertiesAction { GetAccessControl, @@ -445,7 +473,8 @@ namespace Azure { namespace Storage { namespace DataLake { Unknown }; - static std::string PathGetPropertiesActionToString(const PathGetPropertiesAction& pathGetPropertiesAction) + inline std::string PathGetPropertiesActionToString( + const PathGetPropertiesAction& pathGetPropertiesAction) { switch (pathGetPropertiesAction) { @@ -458,7 +487,8 @@ namespace Azure { namespace Storage { namespace DataLake { } } - static PathGetPropertiesAction PathGetPropertiesActionFromString(const std::string& pathGetPropertiesAction) + inline PathGetPropertiesAction PathGetPropertiesActionFromString( + const std::string& pathGetPropertiesAction) { if (pathGetPropertiesAction == "getAccessControl") { @@ -468,2198 +498,3006 @@ namespace Azure { namespace Storage { namespace DataLake { { return PathGetPropertiesAction::GetStatus; } - throw std::runtime_error("Cannot convert " + pathGetPropertiesAction + " to PathGetPropertiesAction"); + throw std::runtime_error( + "Cannot convert " + pathGetPropertiesAction + " to PathGetPropertiesAction"); + } + + struct ServiceListFileSystemsResponse + { + std::string Date; + std::string RequestId; + std::string Version; + std::string Continuation; + std::string ContentType; + std::vector Filesystems; + + static ServiceListFileSystemsResponse ServiceListFileSystemsResponseFromFileSystemList( + FileSystemList object) + { + ServiceListFileSystemsResponse result; + result.Filesystems = std::move(object.Filesystems); + + return result; + } + }; + + struct FileSystemCreateResponse + { + std::string Date; + std::string ETag; + std::string LastModified; + std::string ClientRequestId; + std::string Version; + std::string NamespaceEnabled; + }; + + struct FileSystemSetPropertiesResponse + { + std::string Date; + std::string ETag; + std::string LastModified; + std::string RequestId; + std::string Version; + }; + + struct FileSystemGetPropertiesResponse + { + std::string Date; + std::string ETag; + std::string LastModified; + std::string RequestId; + std::string Version; + std::string Properties; + std::string NamespaceEnabled; + }; + + struct FileSystemDeleteResponse + { + std::string RequestId; + std::string Version; + std::string Date; + }; + + struct FileSystemListPathsResponse + { + std::string Date; + std::string ETag; + std::string LastModified; + std::string RequestId; + std::string Version; + std::string Continuation; + std::vector Paths; + + static FileSystemListPathsResponse FileSystemListPathsResponseFromPathList(PathList object) + { + FileSystemListPathsResponse result; + result.Paths = std::move(object.Paths); + + return result; + } + }; + + struct PathCreateResponse + { + std::string Date; + std::string ETag; + std::string LastModified; + std::string RequestId; + std::string Version; + std::string Continuation; + int64_t ContentLength = int64_t(); + }; + + struct PathUpdateResponse + { + std::string Date; + std::string ETag; + std::string LastModified; + std::string AcceptRanges; + std::string CacheControl; + std::string ContentDisposition; + std::string ContentEncoding; + std::string ContentLanguage; + int64_t ContentLength = int64_t(); + std::string ContentRange; + std::string ContentType; + std::string ContentMD5; + std::string Properties; + std::string XMsContinuation; + std::string RequestId; + std::string Version; + int32_t DirectoriesSuccessful = int32_t(); + int32_t FilesSuccessful = int32_t(); + int32_t FailureCount = int32_t(); + std::vector FailedEntries; + + static PathUpdateResponse PathUpdateResponseFromSetAccessControlRecursiveResponse( + SetAccessControlRecursiveResponse object) + { + PathUpdateResponse result; + result.DirectoriesSuccessful = object.DirectoriesSuccessful; + result.FilesSuccessful = object.FilesSuccessful; + result.FailureCount = object.FailureCount; + result.FailedEntries = std::move(object.FailedEntries); + + return result; + } + }; + + struct PathLeaseResponse + { + std::string Date; + std::string ETag; + std::string LastModified; + std::string RequestId; + std::string Version; + std::string LeaseId; + std::string LeaseTime; + }; + + struct PathReadResponse + { + Azure::Core::Http::BodyStream* BodyStream; + std::string AcceptRanges; + std::string CacheControl; + std::string ContentDisposition; + std::string ContentEncoding; + std::string ContentLanguage; + int64_t ContentLength = int64_t(); + std::string ContentRange; + std::string ContentType; + std::string ContentMD5; + std::string Date; + std::string ETag; + std::string LastModified; + std::string RequestId; + std::string Version; + std::string ResourceType; + std::string Properties; + std::string LeaseDuration; + std::string LeaseState; + std::string LeaseStatus; + std::string XMsContentMd5; + }; + + struct PathGetPropertiesResponse + { + std::string AcceptRanges; + std::string CacheControl; + std::string ContentDisposition; + std::string ContentEncoding; + std::string ContentLanguage; + int64_t ContentLength = int64_t(); + std::string ContentRange; + std::string ContentType; + std::string ContentMD5; + std::string Date; + std::string ETag; + std::string LastModified; + std::string RequestId; + std::string Version; + std::string ResourceType; + std::string Properties; + std::string Owner; + std::string Group; + std::string Permissions; + std::string ACL; + std::string LeaseDuration; + std::string LeaseState; + std::string LeaseStatus; + }; + + struct PathDeleteResponse + { + std::string Date; + std::string RequestId; + std::string Version; + std::string Continuation; + }; + + struct PathSetAccessControlResponse + { + std::string Date; + std::string ETag; + std::string LastModified; + std::string ClientRequestId; + std::string RequestId; + std::string Version; + }; + + struct PathSetAccessControlRecursiveResponse + { + std::string Date; + std::string ClientRequestId; + std::string Continuation; + std::string RequestId; + std::string Version; + int32_t DirectoriesSuccessful = int32_t(); + int32_t FilesSuccessful = int32_t(); + int32_t FailureCount = int32_t(); + std::vector FailedEntries; + + static PathSetAccessControlRecursiveResponse + PathSetAccessControlRecursiveResponseFromSetAccessControlRecursiveResponse( + SetAccessControlRecursiveResponse object) + { + PathSetAccessControlRecursiveResponse result; + result.DirectoriesSuccessful = object.DirectoriesSuccessful; + result.FilesSuccessful = object.FilesSuccessful; + result.FailureCount = object.FailureCount; + result.FailedEntries = std::move(object.FailedEntries); + + return result; + } + }; + + struct PathFlushDataResponse + { + std::string Date; + std::string ETag; + std::string LastModified; + int64_t ContentLength = int64_t(); + std::string ClientRequestId; + std::string RequestId; + std::string Version; + }; + + struct PathAppendDataResponse + { + std::string Date; + std::string RequestId; + std::string ClientRequestId; + std::string Version; }; class DataLakeRestClient { public: - struct ServiceListFileSystemsOptions - { - std::string Prefix; // Filters results to filesystems within the specified prefix. - std::string Continuation; // Optional. When deleting a directory, the number of paths that are deleted with each invocation is limited. If the number of paths to be deleted exceeds this limit, a continuation token is returned in this response header. When a continuation token is returned in the response, it must be specified in a subsequent invocation of the delete operation to continue deleting the directory. - uint32_t MaxResults = uint32_t(); // An optional value that specifies the maximum number of items to return. If omitted or greater than 5,000, the response will include up to 5,000 items. - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - }; - - static Azure::Core::Http::Request ServiceListFileSystemsCreateRequest(std::string url, const ServiceListFileSystemsOptions& serviceListFileSystemsOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Get, url); - request.AddQueryParameter(k_QueryResource, "account"); - if (!serviceListFileSystemsOptions.Prefix.empty()) + class Service { + public: + struct ListFileSystemsOptions { - request.AddQueryParameter(k_QueryPrefix, serviceListFileSystemsOptions.Prefix); + std::string Prefix; // Filters results to filesystems within the specified prefix. + std::string + Continuation; // Optional. When deleting a directory, the number of paths that are + // deleted with each invocation is limited. If the number of paths to be + // deleted exceeds this limit, a continuation token is returned in this + // response header. When a continuation token is returned in the + // response, it must be specified in a subsequent invocation of the delete + // operation to continue deleting the directory. + int32_t MaxResults = int32_t(); // An optional value that specifies the maximum number of + // items to return. If omitted or greater than 5,000, the + // response will include up to 5,000 items. + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + }; + + static ServiceListFileSystemsResponse ListFileSystems( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const ListFileSystemsOptions& listFileSystemsOptions) + { + auto request = ListFileSystemsCreateRequest(std::move(url), listFileSystemsOptions); + return ListFileSystemsParseResponse(pipeline.Send(context, request)); } - if (!serviceListFileSystemsOptions.Continuation.empty()) + + private: + static Azure::Core::Http::Request ListFileSystemsCreateRequest( + std::string url, + const ListFileSystemsOptions& listFileSystemsOptions) { - request.AddQueryParameter(k_QueryContinuation, serviceListFileSystemsOptions.Continuation); + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Get, url); + request.AddQueryParameter(Details::c_QueryResource, "account"); + if (!listFileSystemsOptions.Prefix.empty()) + { + request.AddQueryParameter(Details::c_QueryPrefix, listFileSystemsOptions.Prefix); + } + if (!listFileSystemsOptions.Continuation.empty()) + { + request.AddQueryParameter( + Details::c_QueryContinuation, listFileSystemsOptions.Continuation); + } + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryMaxResults, std::to_string(listFileSystemsOptions.MaxResults)); + if (!listFileSystemsOptions.ClientRequestId.empty()) + { + request.AddHeader( + Details::c_HeaderClientRequestId, listFileSystemsOptions.ClientRequestId); + } + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryTimeout, std::to_string(listFileSystemsOptions.Timeout)); + request.AddHeader( + Details::c_HeaderApiVersionParameter, listFileSystemsOptions.ApiVersionParameter); + return request; } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryMaxResults, std::to_string(serviceListFileSystemsOptions.MaxResults)); - if (!serviceListFileSystemsOptions.ClientRequestId.empty()) + + static ServiceListFileSystemsResponse ListFileSystemsParseResponse( + std::unique_ptr responsePtr) { - request.AddHeader(k_HeaderClientRequestId, serviceListFileSystemsOptions.ClientRequestId); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(serviceListFileSystemsOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, serviceListFileSystemsOptions.ApiVersionParameter); - return request; - } - - struct ServiceListFileSystemsResponse - { - std::string Date; - std::string RequestId; - std::string Version; - std::string Continuation; - std::string ContentType; - std::vector Filesystems; - - static ServiceListFileSystemsResponse ServiceListFileSystemsResponseFromFileSystemList(FileSystemList object) - { - ServiceListFileSystemsResponse result; - result.Filesystems = std::move(object.Filesystems); - - return result; + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + { + // OK + ServiceListFileSystemsResponse result + = ServiceListFileSystemsResponse::ServiceListFileSystemsResponseFromFileSystemList( + FileSystemList::CreateFromJson(nlohmann::json::parse( + *Azure::Core::Http::Response::ConstructBodyBufferFromStream( + response.GetBodyStream())))); + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsContinuation) + != response.GetHeaders().end()) + { + result.Continuation = response.GetHeaders().at(Details::c_HeaderXMsContinuation); + } + if (response.GetHeaders().find(Details::c_HeaderContentType) + != response.GetHeaders().end()) + { + result.ContentType = response.GetHeaders().at(Details::c_HeaderContentType); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } } }; - static ServiceListFileSystemsResponse ServiceListFileSystemsParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + class FileSystem { + public: + struct CreateOptions { - // OK - ServiceListFileSystemsResponse result = ServiceListFileSystemsResponse::ServiceListFileSystemsResponseFromFileSystemList(std::move(FileSystemList::CreateFromJson(nlohmann::json::parse(response.GetBodyBuffer())))); - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - if (response.GetHeaders().find(k_HeaderXMsContinuation) != response.GetHeaders().end()) - { - result.Continuation = response.GetHeaders().at(k_HeaderXMsContinuation); - } - if (response.GetHeaders().find(k_HeaderContentType) != response.GetHeaders().end()) - { - result.ContentType = response.GetHeaders().at(k_HeaderContentType); - } - return result; + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + std::string + Properties; // Optional. User-defined properties to be stored with the filesystem, in + // the format of a comma-separated list of name and value pairs "n1=v1, + // n2=v2, ...", where each value is a base64 encoded string. Note that the + // string may only contain ASCII characters in the ISO-8859-1 character set. + // If the filesystem exists, any properties not included in the list will be + // removed. All properties are removed if the header is omitted. To merge + // new and existing properties, first get all existing properties and the + // current E-Tag, then make a conditional request with the E-Tag and include + // values for all properties. + }; + + static FileSystemCreateResponse Create( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const CreateOptions& createOptions) + { + auto request = CreateCreateRequest(std::move(url), createOptions); + return CreateParseResponse(pipeline.Send(context, request)); } - else + + struct SetPropertiesOptions { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + std::string + Properties; // Optional. User-defined properties to be stored with the filesystem, in + // the format of a comma-separated list of name and value pairs "n1=v1, + // n2=v2, ...", where each value is a base64 encoded string. Note that the + // string may only contain ASCII characters in the ISO-8859-1 character set. + // If the filesystem exists, any properties not included in the list will be + // removed. All properties are removed if the header is omitted. To merge + // new and existing properties, first get all existing properties and the + // current E-Tag, then make a conditional request with the E-Tag and include + // values for all properties. + std::string IfModifiedSince; // Specify this header value to operate only on a blob if it + // has been modified since the specified date/time. + std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + }; + + static FileSystemSetPropertiesResponse SetProperties( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const SetPropertiesOptions& setPropertiesOptions) + { + auto request = SetPropertiesCreateRequest(std::move(url), setPropertiesOptions); + return SetPropertiesParseResponse(pipeline.Send(context, request)); } - } - static ServiceListFileSystemsResponse ServiceListFileSystems(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const ServiceListFileSystemsOptions& serviceListFileSystemsOptions) - { - // TODO: Pipeline will be added when available. - return ServiceListFileSystemsParseResponse(transport->Send(context, ServiceListFileSystemsCreateRequest(std::move(url), serviceListFileSystemsOptions))); - } - - struct FileSystemCreateOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - std::string Properties; // Optional. User-defined properties to be stored with the filesystem, in the format of a comma-separated list of name and value pairs "n1=v1, n2=v2, ...", where each value is a base64 encoded string. Note that the string may only contain ASCII characters in the ISO-8859-1 character set. If the filesystem exists, any properties not included in the list will be removed. All properties are removed if the header is omitted. To merge new and existing properties, first get all existing properties and the current E-Tag, then make a conditional request with the E-Tag and include values for all properties. - }; - - static Azure::Core::Http::Request FileSystemCreateCreateRequest(std::string url, const FileSystemCreateOptions& fileSystemCreateOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Put, url); - request.AddQueryParameter(k_QueryFileSystemResource, "filesystem"); - if (!fileSystemCreateOptions.ClientRequestId.empty()) + struct GetPropertiesOptions { - request.AddHeader(k_HeaderClientRequestId, fileSystemCreateOptions.ClientRequestId); + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + }; + + static FileSystemGetPropertiesResponse GetProperties( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const GetPropertiesOptions& getPropertiesOptions) + { + auto request = GetPropertiesCreateRequest(std::move(url), getPropertiesOptions); + return GetPropertiesParseResponse(pipeline.Send(context, request)); } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(fileSystemCreateOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, fileSystemCreateOptions.ApiVersionParameter); - if (!fileSystemCreateOptions.Properties.empty()) + + struct DeleteOptions { - request.AddHeader(k_HeaderProperties, fileSystemCreateOptions.Properties); + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + std::string IfModifiedSince; // Specify this header value to operate only on a blob if it + // has been modified since the specified date/time. + std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + }; + + static FileSystemDeleteResponse Delete( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const DeleteOptions& deleteOptions) + { + auto request = DeleteCreateRequest(std::move(url), deleteOptions); + return DeleteParseResponse(pipeline.Send(context, request)); } - return request; - } - struct FileSystemCreateResponse - { - std::string Date; - std::string ETag; - std::string LastModified; - std::string ClientRequestId; - std::string Version; - std::string NamespaceEnabled; - }; - - static FileSystemCreateResponse FileSystemCreateParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Created) + struct ListPathsOptions { - // Created - FileSystemCreateResponse result; - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + std::string + Continuation; // Optional. When deleting a directory, the number of paths that are + // deleted with each invocation is limited. If the number of paths to be + // deleted exceeds this limit, a continuation token is returned in this + // response header. When a continuation token is returned in the + // response, it must be specified in a subsequent invocation of the delete + // operation to continue deleting the directory. + std::string Directory; // Optional. Filters results to paths within the specified + // directory. An error occurs if the directory does not exist. + bool RecursiveRequired = bool(); // Required + int32_t MaxResults = int32_t(); // An optional value that specifies the maximum number of + // items to return. If omitted or greater than 5,000, the + // response will include up to 5,000 items. + bool Upn + = bool(); // Optional. Valid only when Hierarchical Namespace is enabled for the + // account. If "true", the user identity values returned in the x-ms-owner, + // x-ms-group, and x-ms-acl response headers will be transformed from Azure + // Active Directory Object IDs to User Principal Names. If "false", the + // values will be returned as Azure Active Directory Object IDs. The default + // value is false. Note that group and application Object IDs are not + // translated because they do not have unique friendly names. + }; + + static FileSystemListPathsResponse ListPaths( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const ListPathsOptions& listPathsOptions) + { + auto request = ListPathsCreateRequest(std::move(url), listPathsOptions); + return ListPathsParseResponse(pipeline.Send(context, request)); + } + + private: + static Azure::Core::Http::Request CreateCreateRequest( + std::string url, + const CreateOptions& createOptions) + { + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Put, url); + request.AddQueryParameter(Details::c_QueryFileSystemResource, "filesystem"); + if (!createOptions.ClientRequestId.empty()) { - result.Date = response.GetHeaders().at(k_HeaderDate); + request.AddHeader(Details::c_HeaderClientRequestId, createOptions.ClientRequestId); } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter(Details::c_QueryTimeout, std::to_string(createOptions.Timeout)); + request.AddHeader(Details::c_HeaderApiVersionParameter, createOptions.ApiVersionParameter); + if (!createOptions.Properties.empty()) { - result.ETag = response.GetHeaders().at(k_HeaderETag); + request.AddHeader(Details::c_HeaderProperties, createOptions.Properties); } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.ClientRequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - if (response.GetHeaders().find(k_HeaderXMsNamespaceEnabled) != response.GetHeaders().end()) - { - result.NamespaceEnabled = response.GetHeaders().at(k_HeaderXMsNamespaceEnabled); - } - return result; + return request; } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - static FileSystemCreateResponse FileSystemCreate(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const FileSystemCreateOptions& fileSystemCreateOptions) - { - // TODO: Pipeline will be added when available. - return FileSystemCreateParseResponse(transport->Send(context, FileSystemCreateCreateRequest(std::move(url), fileSystemCreateOptions))); - } - - struct FileSystemSetPropertiesOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - std::string Properties; // Optional. User-defined properties to be stored with the filesystem, in the format of a comma-separated list of name and value pairs "n1=v1, n2=v2, ...", where each value is a base64 encoded string. Note that the string may only contain ASCII characters in the ISO-8859-1 character set. If the filesystem exists, any properties not included in the list will be removed. All properties are removed if the header is omitted. To merge new and existing properties, first get all existing properties and the current E-Tag, then make a conditional request with the E-Tag and include values for all properties. - std::string IfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - }; - - static Azure::Core::Http::Request FileSystemSetPropertiesCreateRequest(std::string url, const FileSystemSetPropertiesOptions& fileSystemSetPropertiesOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Patch, url); - request.AddQueryParameter(k_QueryFileSystemResource, "filesystem"); - if (!fileSystemSetPropertiesOptions.ClientRequestId.empty()) + static FileSystemCreateResponse CreateParseResponse( + std::unique_ptr responsePtr) { - request.AddHeader(k_HeaderClientRequestId, fileSystemSetPropertiesOptions.ClientRequestId); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(fileSystemSetPropertiesOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, fileSystemSetPropertiesOptions.ApiVersionParameter); - if (!fileSystemSetPropertiesOptions.Properties.empty()) - { - request.AddHeader(k_HeaderProperties, fileSystemSetPropertiesOptions.Properties); - } - if (!fileSystemSetPropertiesOptions.IfModifiedSince.empty()) - { - request.AddHeader(k_HeaderIfModifiedSince, fileSystemSetPropertiesOptions.IfModifiedSince); - } - if (!fileSystemSetPropertiesOptions.IfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderIfUnmodifiedSince, fileSystemSetPropertiesOptions.IfUnmodifiedSince); - } - return request; - } - - struct FileSystemSetPropertiesResponse - { - std::string Date; - std::string ETag; - std::string LastModified; - std::string RequestId; - std::string Version; - }; - - static FileSystemSetPropertiesResponse FileSystemSetPropertiesParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) - { - // Ok - FileSystemSetPropertiesResponse result; - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Created) { - result.Date = response.GetHeaders().at(k_HeaderDate); + // Created + FileSystemCreateResponse result; + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.ClientRequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsNamespaceEnabled) + != response.GetHeaders().end()) + { + result.NamespaceEnabled + = response.GetHeaders().at(Details::c_HeaderXMsNamespaceEnabled); + } + return result; } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) + else { - result.ETag = response.GetHeaders().at(k_HeaderETag); + throw Azure::Storage::StorageError::CreateFromResponse(response); } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - return result; } - else + + static Azure::Core::Http::Request SetPropertiesCreateRequest( + std::string url, + const SetPropertiesOptions& setPropertiesOptions) { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static FileSystemSetPropertiesResponse FileSystemSetProperties(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const FileSystemSetPropertiesOptions& fileSystemSetPropertiesOptions) - { - // TODO: Pipeline will be added when available. - return FileSystemSetPropertiesParseResponse(transport->Send(context, FileSystemSetPropertiesCreateRequest(std::move(url), fileSystemSetPropertiesOptions))); - } - - struct FileSystemGetPropertiesOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - }; - - static Azure::Core::Http::Request FileSystemGetPropertiesCreateRequest(std::string url, const FileSystemGetPropertiesOptions& fileSystemGetPropertiesOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Head, url); - request.AddQueryParameter(k_QueryFileSystemResource, "filesystem"); - if (!fileSystemGetPropertiesOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, fileSystemGetPropertiesOptions.ClientRequestId); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(fileSystemGetPropertiesOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, fileSystemGetPropertiesOptions.ApiVersionParameter); - return request; - } - - struct FileSystemGetPropertiesResponse - { - std::string Date; - std::string ETag; - std::string LastModified; - std::string RequestId; - std::string Version; - std::string Properties; - std::string NamespaceEnabled; - }; - - static FileSystemGetPropertiesResponse FileSystemGetPropertiesParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) - { - // Ok - FileSystemGetPropertiesResponse result; - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Patch, url); + request.AddQueryParameter(Details::c_QueryFileSystemResource, "filesystem"); + if (!setPropertiesOptions.ClientRequestId.empty()) { - result.Date = response.GetHeaders().at(k_HeaderDate); + request.AddHeader(Details::c_HeaderClientRequestId, setPropertiesOptions.ClientRequestId); } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryTimeout, std::to_string(setPropertiesOptions.Timeout)); + request.AddHeader( + Details::c_HeaderApiVersionParameter, setPropertiesOptions.ApiVersionParameter); + if (!setPropertiesOptions.Properties.empty()) { - result.ETag = response.GetHeaders().at(k_HeaderETag); + request.AddHeader(Details::c_HeaderProperties, setPropertiesOptions.Properties); } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) + if (!setPropertiesOptions.IfModifiedSince.empty()) { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); + request.AddHeader(Details::c_HeaderIfModifiedSince, setPropertiesOptions.IfModifiedSince); } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) + if (!setPropertiesOptions.IfUnmodifiedSince.empty()) { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); + request.AddHeader( + Details::c_HeaderIfUnmodifiedSince, setPropertiesOptions.IfUnmodifiedSince); } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) + return request; + } + + static FileSystemSetPropertiesResponse SetPropertiesParseResponse( + std::unique_ptr responsePtr) + { + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); + // Ok + FileSystemSetPropertiesResponse result; + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + return result; } - if (response.GetHeaders().find(k_HeaderXMsProperties) != response.GetHeaders().end()) + else { - result.Properties = response.GetHeaders().at(k_HeaderXMsProperties); + throw Azure::Storage::StorageError::CreateFromResponse(response); } - if (response.GetHeaders().find(k_HeaderXMsNamespaceEnabled) != response.GetHeaders().end()) + } + + static Azure::Core::Http::Request GetPropertiesCreateRequest( + std::string url, + const GetPropertiesOptions& getPropertiesOptions) + { + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Head, url); + request.AddQueryParameter(Details::c_QueryFileSystemResource, "filesystem"); + if (!getPropertiesOptions.ClientRequestId.empty()) { - result.NamespaceEnabled = response.GetHeaders().at(k_HeaderXMsNamespaceEnabled); + request.AddHeader(Details::c_HeaderClientRequestId, getPropertiesOptions.ClientRequestId); } - return result; + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryTimeout, std::to_string(getPropertiesOptions.Timeout)); + request.AddHeader( + Details::c_HeaderApiVersionParameter, getPropertiesOptions.ApiVersionParameter); + return request; } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - static FileSystemGetPropertiesResponse FileSystemGetProperties(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const FileSystemGetPropertiesOptions& fileSystemGetPropertiesOptions) - { - // TODO: Pipeline will be added when available. - return FileSystemGetPropertiesParseResponse(transport->Send(context, FileSystemGetPropertiesCreateRequest(std::move(url), fileSystemGetPropertiesOptions))); - } - - struct FileSystemDeleteOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - std::string IfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - }; - - static Azure::Core::Http::Request FileSystemDeleteCreateRequest(std::string url, const FileSystemDeleteOptions& fileSystemDeleteOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Delete, url); - request.AddQueryParameter(k_QueryFileSystemResource, "filesystem"); - if (!fileSystemDeleteOptions.ClientRequestId.empty()) + static FileSystemGetPropertiesResponse GetPropertiesParseResponse( + std::unique_ptr responsePtr) { - request.AddHeader(k_HeaderClientRequestId, fileSystemDeleteOptions.ClientRequestId); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(fileSystemDeleteOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, fileSystemDeleteOptions.ApiVersionParameter); - if (!fileSystemDeleteOptions.IfModifiedSince.empty()) - { - request.AddHeader(k_HeaderIfModifiedSince, fileSystemDeleteOptions.IfModifiedSince); - } - if (!fileSystemDeleteOptions.IfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderIfUnmodifiedSince, fileSystemDeleteOptions.IfUnmodifiedSince); - } - return request; - } - - struct FileSystemDeleteResponse - { - std::string RequestId; - std::string Version; - std::string Date; - }; - - static FileSystemDeleteResponse FileSystemDeleteParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Accepted) - { - // Accepted - FileSystemDeleteResponse result; - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); + // Ok + FileSystemGetPropertiesResponse result; + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsProperties) + != response.GetHeaders().end()) + { + result.Properties = response.GetHeaders().at(Details::c_HeaderXMsProperties); + } + if (response.GetHeaders().find(Details::c_HeaderXMsNamespaceEnabled) + != response.GetHeaders().end()) + { + result.NamespaceEnabled + = response.GetHeaders().at(Details::c_HeaderXMsNamespaceEnabled); + } + return result; } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) + else { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); + throw Azure::Storage::StorageError::CreateFromResponse(response); } - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) + } + + static Azure::Core::Http::Request DeleteCreateRequest( + std::string url, + const DeleteOptions& deleteOptions) + { + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Delete, url); + request.AddQueryParameter(Details::c_QueryFileSystemResource, "filesystem"); + if (!deleteOptions.ClientRequestId.empty()) { - result.Date = response.GetHeaders().at(k_HeaderDate); + request.AddHeader(Details::c_HeaderClientRequestId, deleteOptions.ClientRequestId); } - return result; + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter(Details::c_QueryTimeout, std::to_string(deleteOptions.Timeout)); + request.AddHeader(Details::c_HeaderApiVersionParameter, deleteOptions.ApiVersionParameter); + if (!deleteOptions.IfModifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfModifiedSince, deleteOptions.IfModifiedSince); + } + if (!deleteOptions.IfUnmodifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfUnmodifiedSince, deleteOptions.IfUnmodifiedSince); + } + return request; } - else + + static FileSystemDeleteResponse DeleteParseResponse( + std::unique_ptr responsePtr) { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Accepted) + { + // Accepted + FileSystemDeleteResponse result; + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } } - } - static FileSystemDeleteResponse FileSystemDelete(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const FileSystemDeleteOptions& fileSystemDeleteOptions) - { - // TODO: Pipeline will be added when available. - return FileSystemDeleteParseResponse(transport->Send(context, FileSystemDeleteCreateRequest(std::move(url), fileSystemDeleteOptions))); - } - - struct FileSystemListPathsOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - std::string Continuation; // Optional. When deleting a directory, the number of paths that are deleted with each invocation is limited. If the number of paths to be deleted exceeds this limit, a continuation token is returned in this response header. When a continuation token is returned in the response, it must be specified in a subsequent invocation of the delete operation to continue deleting the directory. - std::string Directory; // Optional. Filters results to paths within the specified directory. An error occurs if the directory does not exist. - bool RecursiveRequired = bool(); // Required - uint32_t MaxResults = uint32_t(); // An optional value that specifies the maximum number of items to return. If omitted or greater than 5,000, the response will include up to 5,000 items. - bool Upn = bool(); // Optional. Valid only when Hierarchical Namespace is enabled for the account. If "true", the user identity values returned in the x-ms-owner, x-ms-group, and x-ms-acl response headers will be transformed from Azure Active Directory Object IDs to User Principal Names. If "false", the values will be returned as Azure Active Directory Object IDs. The default value is false. Note that group and application Object IDs are not translated because they do not have unique friendly names. - }; - - static Azure::Core::Http::Request FileSystemListPathsCreateRequest(std::string url, const FileSystemListPathsOptions& fileSystemListPathsOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Get, url); - request.AddQueryParameter(k_QueryFileSystemResource, "filesystem"); - if (!fileSystemListPathsOptions.ClientRequestId.empty()) + static Azure::Core::Http::Request ListPathsCreateRequest( + std::string url, + const ListPathsOptions& listPathsOptions) { - request.AddHeader(k_HeaderClientRequestId, fileSystemListPathsOptions.ClientRequestId); + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Get, url); + request.AddQueryParameter(Details::c_QueryFileSystemResource, "filesystem"); + if (!listPathsOptions.ClientRequestId.empty()) + { + request.AddHeader(Details::c_HeaderClientRequestId, listPathsOptions.ClientRequestId); + } + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryTimeout, std::to_string(listPathsOptions.Timeout)); + request.AddHeader( + Details::c_HeaderApiVersionParameter, listPathsOptions.ApiVersionParameter); + if (!listPathsOptions.Continuation.empty()) + { + request.AddQueryParameter(Details::c_QueryContinuation, listPathsOptions.Continuation); + } + if (!listPathsOptions.Directory.empty()) + { + request.AddQueryParameter(Details::c_QueryDirectory, listPathsOptions.Directory); + } + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryRecursiveRequired, + (listPathsOptions.RecursiveRequired ? "true" : "false")); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryMaxResults, std::to_string(listPathsOptions.MaxResults)); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter(Details::c_QueryUpn, (listPathsOptions.Upn ? "true" : "false")); + return request; } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(fileSystemListPathsOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, fileSystemListPathsOptions.ApiVersionParameter); - if (!fileSystemListPathsOptions.Continuation.empty()) + + static FileSystemListPathsResponse ListPathsParseResponse( + std::unique_ptr responsePtr) { - request.AddQueryParameter(k_QueryContinuation, fileSystemListPathsOptions.Continuation); - } - if (!fileSystemListPathsOptions.Directory.empty()) - { - request.AddQueryParameter(k_QueryDirectory, fileSystemListPathsOptions.Directory); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryRecursiveRequired, (fileSystemListPathsOptions.RecursiveRequired ? "true" : "false")); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryMaxResults, std::to_string(fileSystemListPathsOptions.MaxResults)); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryUpn, (fileSystemListPathsOptions.Upn ? "true" : "false")); - return request; - } - - struct FileSystemListPathsResponse - { - std::string Date; - std::string ETag; - std::string LastModified; - std::string RequestId; - std::string Version; - std::string Continuation; - std::vector Paths; - - static FileSystemListPathsResponse FileSystemListPathsResponseFromPathList(PathList object) - { - FileSystemListPathsResponse result; - result.Paths = std::move(object.Paths); - - return result; + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + { + // Ok + FileSystemListPathsResponse result + = FileSystemListPathsResponse::FileSystemListPathsResponseFromPathList( + PathList::CreateFromJson(nlohmann::json::parse( + *Azure::Core::Http::Response::ConstructBodyBufferFromStream( + response.GetBodyStream())))); + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsContinuation) + != response.GetHeaders().end()) + { + result.Continuation = response.GetHeaders().at(Details::c_HeaderXMsContinuation); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } } }; - static FileSystemListPathsResponse FileSystemListPathsParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + class Path { + public: + struct CreateOptions { - // Ok - FileSystemListPathsResponse result = FileSystemListPathsResponse::FileSystemListPathsResponseFromPathList(std::move(PathList::CreateFromJson(nlohmann::json::parse(response.GetBodyBuffer())))); - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + PathResourceType Resource + = PathResourceType::Unknown; // Required only for Create File and Create Directory. The + // value must be "file" or "directory". + std::string + Continuation; // Optional. When deleting a directory, the number of paths that are + // deleted with each invocation is limited. If the number of paths to be + // deleted exceeds this limit, a continuation token is returned in this + // response header. When a continuation token is returned in the + // response, it must be specified in a subsequent invocation of the delete + // operation to continue deleting the directory. + PathRenameMode Mode + = PathRenameMode::Unknown; // Optional. Valid only when namespace is enabled. This + // parameter determines the behavior of the rename operation. + // The value must be "legacy" or "posix", and the default + // value will be "posix". + std::string + CacheControl; // Optional. Sets the blob's cache control. If specified, this property is + // stored with the blob and returned with a read request. + std::string + ContentEncoding; // Optional. Sets the blob's content encoding. If specified, this + // property is stored with the blob and returned with a read request. + std::string + ContentLanguage; // Optional. Set the blob's content language. If specified, this + // property is stored with the blob and returned with a read request. + std::string ContentDisposition; // Optional. Sets the blob's Content-Disposition header. + std::string + ContentType; // Optional. Sets the blob's content type. If specified, this property is + // stored with the blob and returned with a read request. + std::string + RenameSource; // An optional file or directory to be renamed. The value must have the + // following format: "/{filesystem}/{path}". If "x-ms-properties" is + // specified, the properties will overwrite the existing properties; + // otherwise, the existing properties will be preserved. This value must + // be a URL percent-encoded string. Note that the string may only contain + // ASCII characters in the ISO-8859-1 character set. + std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's + // lease is active and matches this ID. + std::string SourceLeaseId; // A lease ID for the source path. If specified, the source path + // must have an active lease and the leaase ID must match. + std::string + Properties; // Optional. User-defined properties to be stored with the filesystem, in + // the format of a comma-separated list of name and value pairs "n1=v1, + // n2=v2, ...", where each value is a base64 encoded string. Note that the + // string may only contain ASCII characters in the ISO-8859-1 character set. + // If the filesystem exists, any properties not included in the list will be + // removed. All properties are removed if the header is omitted. To merge + // new and existing properties, first get all existing properties and the + // current E-Tag, then make a conditional request with the E-Tag and include + // values for all properties. + std::string + Permissions; // Optional and only valid if Hierarchical Namespace is enabled for the + // account. Sets POSIX access permissions for the file owner, the file + // owning group, and others. Each class may be granted read, write, or + // execute permission. The sticky bit is also supported. Both symbolic + // (rwxrw-rw-) and 4-digit octal notation (e.g. 0766) are supported. + std::string + Umask; // Optional and only valid if Hierarchical Namespace is enabled for the account. + // When creating a file or directory and the parent folder does not have a + // default ACL, the umask restricts the permissions of the file or directory to + // be created. The resulting permission is given by p bitwise and not u, where p + // is the permission and u is the umask. For example, if p is 0777 and u is + // 0057, then the resulting permission is 0720. The default permission is 0777 + // for a directory and 0666 for a file. The default umask is 0027. The umask + // must be specified in 4-digit octal notation (e.g. 0766). + std::string + IfMatch; // Specify an ETag value to operate only on blobs with a matching value. + std::string + IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. + std::string IfModifiedSince; // Specify this header value to operate only on a blob if it + // has been modified since the specified date/time. + std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + std::string + SourceIfMatch; // Specify an ETag value to operate only on blobs with a matching value. + std::string SourceIfNoneMatch; // Specify an ETag value to operate only on blobs without a + // matching value. + std::string SourceIfModifiedSince; // Specify this header value to operate only on a blob if + // it has been modified since the specified date/time. + std::string + SourceIfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + }; + + static PathCreateResponse Create( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const CreateOptions& createOptions) + { + auto request = CreateCreateRequest(std::move(url), createOptions); + return CreateParseResponse(pipeline.Send(context, request)); + } + + struct UpdateOptions + { + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + PathUpdateAction + Action; // The action must be "append" to upload data to be appended to a file, "flush" + // to flush previously uploaded data to a file, "setProperties" to set the + // properties of a file or directory, "setAccessControl" to set the owner, + // group, permissions, or access control list for a file or directory, or + // "setAccessControlRecursive" to set the access control list for a directory + // recursively. Note that Hierarchical Namespace must be enabled for the account + // in order to use access control. Also note that the Access Control List (ACL) + // includes permissions for the owner, owning group, and others, so the + // x-ms-permissions and x-ms-acl request headers are mutually exclusive. + int32_t MaxRecords + = int32_t(); // Optional. Valid for "SetAccessControlRecursive" operation. It specifies + // the maximum number of files or directories on which the acl change will + // be applied. If omitted or greater than 2,000, the request will process + // up to 2,000 items + std::string Continuation; // Optional. The number of paths processed with each invocation is + // limited. If the number of paths to be processed exceeds this + // limit, a continuation token is returned in the response header + // x-ms-continuation. When a continuation token is returned in + // the response, it must be percent-encoded and specified in a + // subsequent invocation of setAcessControlRecursive operation. + PathSetAccessControlRecursiveMode + Mode; // Mode "set" sets POSIX access control rights on files and directories, "modify" + // modifies one or more POSIX access control rights that pre-exist on files and + // directories, "remove" removes one or more POSIX access control rights that + // were present earlier on files and directories + int64_t Position + = int64_t(); // This parameter allows the caller to upload data in parallel and control + // the order in which it is appended to the file. It is required when + // uploading data to be appended to the file and when flushing previously + // uploaded data to the file. The value must be the position where the + // data is to be appended. Uploaded data is not immediately flushed, or + // written, to the file. To flush, the previously uploaded data must be + // contiguous, the position parameter must be specified and equal to the + // length of the file after all data has been written, and there must not + // be a request entity body included with the request. + bool RetainUncommittedData + = bool(); // Valid only for flush operations. If "true", uncommitted data is retained + // after the flush operation completes; otherwise, the uncommitted data is + // deleted after the flush operation. The default is false. Data at offsets + // less than the specified position are written to the file when flush + // succeeds, but this optional parameter allows data after the flush position + // to be retained for a future flush operation. + bool Close + = bool(); // Azure Storage Events allow applications to receive notifications when files + // change. When Azure Storage Events are enabled, a file changed event is + // raised. This event has a property indicating whether this is the final + // change to distinguish the difference between an intermediate flush to a + // file stream and the final close of a file stream. The close query parameter + // is valid only when the action is "flush" and change notifications are + // enabled. If the value of close is "true" and the flush operation completes + // successfully, the service raises a file change notification with a property + // indicating that this is the final update (the file stream has been closed). + // If "false" a change notification is raised indicating the file has changed. + // The default is false. This query parameter is set to true by the Hadoop + // ABFS driver to indicate that the file stream has been closed." + int64_t ContentLength = int64_t(); // Required for "Append Data" and "Flush Data". Must be + // 0 for "Flush Data". Must be the length of the request + // content in bytes for "Append Data". + std::string ContentMD5; // Specify the transactional md5 for the body, to be validated by + // the service. + std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's + // lease is active and matches this ID. + std::string + CacheControl; // Optional. Sets the blob's cache control. If specified, this property is + // stored with the blob and returned with a read request. + std::string + ContentType; // Optional. Sets the blob's content type. If specified, this property is + // stored with the blob and returned with a read request. + std::string ContentDisposition; // Optional. Sets the blob's Content-Disposition header. + std::string + ContentEncoding; // Optional. Sets the blob's content encoding. If specified, this + // property is stored with the blob and returned with a read request. + std::string + ContentLanguage; // Optional. Set the blob's content language. If specified, this + // property is stored with the blob and returned with a read request. + std::string + Properties; // Optional. User-defined properties to be stored with the filesystem, in + // the format of a comma-separated list of name and value pairs "n1=v1, + // n2=v2, ...", where each value is a base64 encoded string. Note that the + // string may only contain ASCII characters in the ISO-8859-1 character set. + // If the filesystem exists, any properties not included in the list will be + // removed. All properties are removed if the header is omitted. To merge + // new and existing properties, first get all existing properties and the + // current E-Tag, then make a conditional request with the E-Tag and include + // values for all properties. + std::string Owner; // Optional. The owner of the blob or directory. + std::string Group; // Optional. The owning group of the blob or directory. + std::string + Permissions; // Optional and only valid if Hierarchical Namespace is enabled for the + // account. Sets POSIX access permissions for the file owner, the file + // owning group, and others. Each class may be granted read, write, or + // execute permission. The sticky bit is also supported. Both symbolic + // (rwxrw-rw-) and 4-digit octal notation (e.g. 0766) are supported. + std::string Acl; // Sets POSIX access control rights on files and directories. The value is + // a comma-separated list of access control entries. Each access control + // entry (ACE) consists of a scope, a type, a user or group identifier, and + // permissions in the format "[scope:][type]:[id]:[permissions]". + std::string + IfMatch; // Specify an ETag value to operate only on blobs with a matching value. + std::string + IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. + std::string IfModifiedSince; // Specify this header value to operate only on a blob if it + // has been modified since the specified date/time. + std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + Azure::Core::Http::BodyStream* Body; // The stream that contains the body of this request. + }; + + static PathUpdateResponse Update( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const UpdateOptions& updateOptions) + { + auto request = UpdateCreateRequest(std::move(url), updateOptions); + return UpdateParseResponse(pipeline.Send(context, request)); + } + + struct LeaseOptions + { + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + PathLeaseAction + XMsLeaseAction; // There are five lease actions: "acquire", "break", "change", "renew", + // and "release". Use "acquire" and specify the "x-ms-proposed-lease-id" + // and "x-ms-lease-duration" to acquire a new lease. Use "break" to + // break an existing lease. When a lease is broken, the lease break + // period is allowed to elapse, during which time no lease operation + // except break and release can be performed on the file. When a lease + // is successfully broken, the response indicates the interval in + // seconds until a new lease can be acquired. Use "change" and specify + // the current lease ID in "x-ms-lease-id" and the new lease ID in + // "x-ms-proposed-lease-id" to change the lease ID of an active lease. + // Use "renew" and specify the "x-ms-lease-id" to renew an existing + // lease. Use "release" and specify the "x-ms-lease-id" to release a + // lease. + int32_t XMsLeaseDuration + = int32_t(); // The lease duration is required to acquire a lease, and specifies the + // duration of the lease in seconds. The lease duration must be between 15 + // and 60 seconds or -1 for infinite lease. + int32_t XMsLeaseBreakPeriod + = int32_t(); // The lease break period duration is optional to break a lease, and + // specifies the break period of the lease in seconds. The lease break + // duration must be between 0 and 60 seconds. + std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's + // lease is active and matches this ID. + std::string + ProposedLeaseIdOptional; // Proposed lease ID, in a GUID string format. The Blob service + // returns 400 (Invalid request) if the proposed lease ID is + // not in the correct format. See Guid Constructor (String) for + // a list of valid GUID string formats. + std::string + IfMatch; // Specify an ETag value to operate only on blobs with a matching value. + std::string + IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. + std::string IfModifiedSince; // Specify this header value to operate only on a blob if it + // has been modified since the specified date/time. + std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + }; + + static PathLeaseResponse Lease( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const LeaseOptions& leaseOptions) + { + auto request = LeaseCreateRequest(std::move(url), leaseOptions); + return LeaseParseResponse(pipeline.Send(context, request)); + } + + struct ReadOptions + { + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + std::string Range; // The HTTP Range request header specifies one or more byte ranges of the + // resource to be retrieved. + std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's + // lease is active and matches this ID. + bool XMsRangeGetContentMd5 + = bool(); // Optional. When this header is set to "true" and specified together with the + // Range header, the service returns the MD5 hash for the range, as long as + // the range is less than or equal to 4MB in size. If this header is specified + // without the Range header, the service returns status code 400 (Bad + // Request). If this header is set to true when the range exceeds 4 MB in + // size, the service returns status code 400 (Bad Request). + std::string + IfMatch; // Specify an ETag value to operate only on blobs with a matching value. + std::string + IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. + std::string IfModifiedSince; // Specify this header value to operate only on a blob if it + // has been modified since the specified date/time. + std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + }; + + static PathReadResponse Read( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const ReadOptions& readOptions) + { + auto request = ReadCreateRequest(std::move(url), readOptions); + return ReadParseResponse(pipeline.Send(context, request)); + } + + struct GetPropertiesOptions + { + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + PathGetPropertiesAction Action = PathGetPropertiesAction:: + Unknown; // Optional. If the value is "getStatus" only the system defined properties for + // the path are returned. If the value is "getAccessControl" the access control + // list is returned in the response headers (Hierarchical Namespace must be + // enabled for the account), otherwise the properties are returned. + bool Upn + = bool(); // Optional. Valid only when Hierarchical Namespace is enabled for the + // account. If "true", the user identity values returned in the x-ms-owner, + // x-ms-group, and x-ms-acl response headers will be transformed from Azure + // Active Directory Object IDs to User Principal Names. If "false", the + // values will be returned as Azure Active Directory Object IDs. The default + // value is false. Note that group and application Object IDs are not + // translated because they do not have unique friendly names. + std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's + // lease is active and matches this ID. + std::string + IfMatch; // Specify an ETag value to operate only on blobs with a matching value. + std::string + IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. + std::string IfModifiedSince; // Specify this header value to operate only on a blob if it + // has been modified since the specified date/time. + std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + }; + + static PathGetPropertiesResponse GetProperties( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const GetPropertiesOptions& getPropertiesOptions) + { + auto request = GetPropertiesCreateRequest(std::move(url), getPropertiesOptions); + return GetPropertiesParseResponse(pipeline.Send(context, request)); + } + + struct DeleteOptions + { + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + bool RecursiveOptional = bool(); // Required + std::string + Continuation; // Optional. When deleting a directory, the number of paths that are + // deleted with each invocation is limited. If the number of paths to be + // deleted exceeds this limit, a continuation token is returned in this + // response header. When a continuation token is returned in the + // response, it must be specified in a subsequent invocation of the delete + // operation to continue deleting the directory. + std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's + // lease is active and matches this ID. + std::string + IfMatch; // Specify an ETag value to operate only on blobs with a matching value. + std::string + IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. + std::string IfModifiedSince; // Specify this header value to operate only on a blob if it + // has been modified since the specified date/time. + std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + }; + + static PathDeleteResponse Delete( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const DeleteOptions& deleteOptions) + { + auto request = DeleteCreateRequest(std::move(url), deleteOptions); + return DeleteParseResponse(pipeline.Send(context, request)); + } + + struct SetAccessControlOptions + { + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's + // lease is active and matches this ID. + std::string Owner; // Optional. The owner of the blob or directory. + std::string Group; // Optional. The owning group of the blob or directory. + std::string + Permissions; // Optional and only valid if Hierarchical Namespace is enabled for the + // account. Sets POSIX access permissions for the file owner, the file + // owning group, and others. Each class may be granted read, write, or + // execute permission. The sticky bit is also supported. Both symbolic + // (rwxrw-rw-) and 4-digit octal notation (e.g. 0766) are supported. + std::string Acl; // Sets POSIX access control rights on files and directories. The value is + // a comma-separated list of access control entries. Each access control + // entry (ACE) consists of a scope, a type, a user or group identifier, and + // permissions in the format "[scope:][type]:[id]:[permissions]". + std::string + IfMatch; // Specify an ETag value to operate only on blobs with a matching value. + std::string + IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. + std::string IfModifiedSince; // Specify this header value to operate only on a blob if it + // has been modified since the specified date/time. + std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + }; + + static PathSetAccessControlResponse SetAccessControl( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const SetAccessControlOptions& setAccessControlOptions) + { + auto request = SetAccessControlCreateRequest(std::move(url), setAccessControlOptions); + return SetAccessControlParseResponse(pipeline.Send(context, request)); + } + + struct SetAccessControlRecursiveOptions + { + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + std::string + Continuation; // Optional. When deleting a directory, the number of paths that are + // deleted with each invocation is limited. If the number of paths to be + // deleted exceeds this limit, a continuation token is returned in this + // response header. When a continuation token is returned in the + // response, it must be specified in a subsequent invocation of the delete + // operation to continue deleting the directory. + PathSetAccessControlRecursiveMode + Mode; // Mode "set" sets POSIX access control rights on files and directories, "modify" + // modifies one or more POSIX access control rights that pre-exist on files and + // directories, "remove" removes one or more POSIX access control rights that + // were present earlier on files and directories + int32_t MaxRecords + = int32_t(); // Optional. It specifies the maximum number of files or directories on + // which the acl change will be applied. If omitted or greater than 2,000, + // the request will process up to 2,000 items + std::string Acl; // Sets POSIX access control rights on files and directories. The value is + // a comma-separated list of access control entries. Each access control + // entry (ACE) consists of a scope, a type, a user or group identifier, and + // permissions in the format "[scope:][type]:[id]:[permissions]". + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + }; + + static PathSetAccessControlRecursiveResponse SetAccessControlRecursive( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const SetAccessControlRecursiveOptions& setAccessControlRecursiveOptions) + { + auto request = SetAccessControlRecursiveCreateRequest( + std::move(url), setAccessControlRecursiveOptions); + return SetAccessControlRecursiveParseResponse(pipeline.Send(context, request)); + } + + struct FlushDataOptions + { + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + int64_t Position + = int64_t(); // This parameter allows the caller to upload data in parallel and control + // the order in which it is appended to the file. It is required when + // uploading data to be appended to the file and when flushing previously + // uploaded data to the file. The value must be the position where the + // data is to be appended. Uploaded data is not immediately flushed, or + // written, to the file. To flush, the previously uploaded data must be + // contiguous, the position parameter must be specified and equal to the + // length of the file after all data has been written, and there must not + // be a request entity body included with the request. + bool RetainUncommittedData + = bool(); // Valid only for flush operations. If "true", uncommitted data is retained + // after the flush operation completes; otherwise, the uncommitted data is + // deleted after the flush operation. The default is false. Data at offsets + // less than the specified position are written to the file when flush + // succeeds, but this optional parameter allows data after the flush position + // to be retained for a future flush operation. + bool Close + = bool(); // Azure Storage Events allow applications to receive notifications when files + // change. When Azure Storage Events are enabled, a file changed event is + // raised. This event has a property indicating whether this is the final + // change to distinguish the difference between an intermediate flush to a + // file stream and the final close of a file stream. The close query parameter + // is valid only when the action is "flush" and change notifications are + // enabled. If the value of close is "true" and the flush operation completes + // successfully, the service raises a file change notification with a property + // indicating that this is the final update (the file stream has been closed). + // If "false" a change notification is raised indicating the file has changed. + // The default is false. This query parameter is set to true by the Hadoop + // ABFS driver to indicate that the file stream has been closed." + int64_t ContentLength = int64_t(); // Required for "Append Data" and "Flush Data". Must be + // 0 for "Flush Data". Must be the length of the request + // content in bytes for "Append Data". + std::string ContentMD5; // Specify the transactional md5 for the body, to be validated by + // the service. + std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's + // lease is active and matches this ID. + std::string + CacheControl; // Optional. Sets the blob's cache control. If specified, this property is + // stored with the blob and returned with a read request. + std::string + ContentType; // Optional. Sets the blob's content type. If specified, this property is + // stored with the blob and returned with a read request. + std::string ContentDisposition; // Optional. Sets the blob's Content-Disposition header. + std::string + ContentEncoding; // Optional. Sets the blob's content encoding. If specified, this + // property is stored with the blob and returned with a read request. + std::string + ContentLanguage; // Optional. Set the blob's content language. If specified, this + // property is stored with the blob and returned with a read request. + std::string + IfMatch; // Specify an ETag value to operate only on blobs with a matching value. + std::string + IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. + std::string IfModifiedSince; // Specify this header value to operate only on a blob if it + // has been modified since the specified date/time. + std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it + // has not been modified since the specified date/time. + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + }; + + static PathFlushDataResponse FlushData( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const FlushDataOptions& flushDataOptions) + { + auto request = FlushDataCreateRequest(std::move(url), flushDataOptions); + return FlushDataParseResponse(pipeline.Send(context, request)); + } + + struct AppendDataOptions + { + int64_t Position + = int64_t(); // This parameter allows the caller to upload data in parallel and control + // the order in which it is appended to the file. It is required when + // uploading data to be appended to the file and when flushing previously + // uploaded data to the file. The value must be the position where the + // data is to be appended. Uploaded data is not immediately flushed, or + // written, to the file. To flush, the previously uploaded data must be + // contiguous, the position parameter must be specified and equal to the + // length of the file after all data has been written, and there must not + // be a request entity body included with the request. + int32_t Timeout + = int32_t(); // The timeout parameter is expressed in seconds. For more + // information, see Setting + // Timeouts for Blob Service Operations. + int64_t ContentLength = int64_t(); // Required for "Append Data" and "Flush Data". Must be + // 0 for "Flush Data". Must be the length of the request + // content in bytes for "Append Data". + std::string TransactionalContentMD5; // Specify the transactional md5 for the body, to be + // validated by the service. + std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's + // lease is active and matches this ID. + Azure::Core::Http::BodyStream* Body; // The stream that contains the body of this request. + std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB + // character limit that is recorded in the analytics logs when + // storage analytics logging is enabled. + std::string ApiVersionParameter + = Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use + // for this request. + }; + + static PathAppendDataResponse AppendData( + std::string url, + Azure::Core::Http::HttpPipeline& pipeline, + Azure::Core::Context context, + const AppendDataOptions& appendDataOptions) + { + auto request = AppendDataCreateRequest(std::move(url), appendDataOptions); + return AppendDataParseResponse(pipeline.Send(context, request)); + } + + private: + static Azure::Core::Http::Request CreateCreateRequest( + std::string url, + const CreateOptions& createOptions) + { + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Put, url); + if (!createOptions.ClientRequestId.empty()) { - result.Date = response.GetHeaders().at(k_HeaderDate); + request.AddHeader(Details::c_HeaderClientRequestId, createOptions.ClientRequestId); } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter(Details::c_QueryTimeout, std::to_string(createOptions.Timeout)); + request.AddHeader(Details::c_HeaderApiVersionParameter, createOptions.ApiVersionParameter); + if (createOptions.Resource != PathResourceType::Unknown) { - result.ETag = response.GetHeaders().at(k_HeaderETag); + request.AddQueryParameter( + Details::c_QueryPathResourceType, PathResourceTypeToString(createOptions.Resource)); } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) + if (!createOptions.Continuation.empty()) { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); + request.AddQueryParameter(Details::c_QueryContinuation, createOptions.Continuation); } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) + if (createOptions.Mode != PathRenameMode::Unknown) { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); + request.AddQueryParameter( + Details::c_QueryPathRenameMode, PathRenameModeToString(createOptions.Mode)); } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) + if (!createOptions.CacheControl.empty()) { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); + request.AddHeader(Details::c_HeaderCacheControl, createOptions.CacheControl); } - if (response.GetHeaders().find(k_HeaderXMsContinuation) != response.GetHeaders().end()) + if (!createOptions.ContentEncoding.empty()) { - result.Continuation = response.GetHeaders().at(k_HeaderXMsContinuation); + request.AddHeader(Details::c_HeaderContentEncoding, createOptions.ContentEncoding); } - return result; - } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static FileSystemListPathsResponse FileSystemListPaths(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const FileSystemListPathsOptions& fileSystemListPathsOptions) - { - // TODO: Pipeline will be added when available. - return FileSystemListPathsParseResponse(transport->Send(context, FileSystemListPathsCreateRequest(std::move(url), fileSystemListPathsOptions))); - } - - struct PathCreateOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - PathResourceType Resource = PathResourceType::Unknown; // Required only for Create File and Create Directory. The value must be "file" or "directory". - std::string Continuation; // Optional. When deleting a directory, the number of paths that are deleted with each invocation is limited. If the number of paths to be deleted exceeds this limit, a continuation token is returned in this response header. When a continuation token is returned in the response, it must be specified in a subsequent invocation of the delete operation to continue deleting the directory. - PathRenameMode Mode = PathRenameMode::Unknown; // Optional. Valid only when namespace is enabled. This parameter determines the behavior of the rename operation. The value must be "legacy" or "posix", and the default value will be "posix". - std::string CacheControl; // Optional. Sets the blob's cache control. If specified, this property is stored with the blob and returned with a read request. - std::string ContentEncoding; // Optional. Sets the blob's content encoding. If specified, this property is stored with the blob and returned with a read request. - std::string ContentLanguage; // Optional. Set the blob's content language. If specified, this property is stored with the blob and returned with a read request. - std::string ContentDisposition; // Optional. Sets the blob's Content-Disposition header. - std::string ContentType; // Optional. Sets the blob's content type. If specified, this property is stored with the blob and returned with a read request. - std::string RenameSource; // An optional file or directory to be renamed. The value must have the following format: "/{filesystem}/{path}". If "x-ms-properties" is specified, the properties will overwrite the existing properties; otherwise, the existing properties will be preserved. This value must be a URL percent-encoded string. Note that the string may only contain ASCII characters in the ISO-8859-1 character set. - std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's lease is active and matches this ID. - std::string SourceLeaseId; // A lease ID for the source path. If specified, the source path must have an active lease and the leaase ID must match. - std::string Properties; // Optional. User-defined properties to be stored with the filesystem, in the format of a comma-separated list of name and value pairs "n1=v1, n2=v2, ...", where each value is a base64 encoded string. Note that the string may only contain ASCII characters in the ISO-8859-1 character set. If the filesystem exists, any properties not included in the list will be removed. All properties are removed if the header is omitted. To merge new and existing properties, first get all existing properties and the current E-Tag, then make a conditional request with the E-Tag and include values for all properties. - std::string Permissions; // Optional and only valid if Hierarchical Namespace is enabled for the account. Sets POSIX access permissions for the file owner, the file owning group, and others. Each class may be granted read, write, or execute permission. The sticky bit is also supported. Both symbolic (rwxrw-rw-) and 4-digit octal notation (e.g. 0766) are supported. - std::string Umask; // Optional and only valid if Hierarchical Namespace is enabled for the account. When creating a file or directory and the parent folder does not have a default ACL, the umask restricts the permissions of the file or directory to be created. The resulting permission is given by p bitwise and not u, where p is the permission and u is the umask. For example, if p is 0777 and u is 0057, then the resulting permission is 0720. The default permission is 0777 for a directory and 0666 for a file. The default umask is 0027. The umask must be specified in 4-digit octal notation (e.g. 0766). - std::string IfMatch; // Specify an ETag value to operate only on blobs with a matching value. - std::string IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. - std::string IfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - std::string SourceIfMatch; // Specify an ETag value to operate only on blobs with a matching value. - std::string SourceIfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. - std::string SourceIfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string SourceIfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - }; - - static Azure::Core::Http::Request PathCreateCreateRequest(std::string url, const PathCreateOptions& pathCreateOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Put, url); - if (!pathCreateOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, pathCreateOptions.ClientRequestId); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(pathCreateOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, pathCreateOptions.ApiVersionParameter); - if (pathCreateOptions.Resource != PathResourceType::Unknown) - { - request.AddQueryParameter(k_QueryPathResourceType, PathResourceTypeToString(pathCreateOptions.Resource)); - } - if (!pathCreateOptions.Continuation.empty()) - { - request.AddQueryParameter(k_QueryContinuation, pathCreateOptions.Continuation); - } - if (pathCreateOptions.Mode != PathRenameMode::Unknown) - { - request.AddQueryParameter(k_QueryPathRenameMode, PathRenameModeToString(pathCreateOptions.Mode)); - } - if (!pathCreateOptions.CacheControl.empty()) - { - request.AddHeader(k_HeaderCacheControl, pathCreateOptions.CacheControl); - } - if (!pathCreateOptions.ContentEncoding.empty()) - { - request.AddHeader(k_HeaderContentEncoding, pathCreateOptions.ContentEncoding); - } - if (!pathCreateOptions.ContentLanguage.empty()) - { - request.AddHeader(k_HeaderContentLanguage, pathCreateOptions.ContentLanguage); - } - if (!pathCreateOptions.ContentDisposition.empty()) - { - request.AddHeader(k_HeaderContentDisposition, pathCreateOptions.ContentDisposition); - } - if (!pathCreateOptions.ContentType.empty()) - { - request.AddHeader(k_HeaderContentType, pathCreateOptions.ContentType); - } - if (!pathCreateOptions.RenameSource.empty()) - { - request.AddHeader(k_HeaderRenameSource, pathCreateOptions.RenameSource); - } - if (!pathCreateOptions.LeaseIdOptional.empty()) - { - request.AddHeader(k_HeaderLeaseIdOptional, pathCreateOptions.LeaseIdOptional); - } - if (!pathCreateOptions.SourceLeaseId.empty()) - { - request.AddHeader(k_HeaderSourceLeaseId, pathCreateOptions.SourceLeaseId); - } - if (!pathCreateOptions.Properties.empty()) - { - request.AddHeader(k_HeaderProperties, pathCreateOptions.Properties); - } - if (!pathCreateOptions.Permissions.empty()) - { - request.AddHeader(k_HeaderPermissions, pathCreateOptions.Permissions); - } - if (!pathCreateOptions.Umask.empty()) - { - request.AddHeader(k_HeaderUmask, pathCreateOptions.Umask); - } - if (!pathCreateOptions.IfMatch.empty()) - { - request.AddHeader(k_HeaderIfMatch, pathCreateOptions.IfMatch); - } - if (!pathCreateOptions.IfNoneMatch.empty()) - { - request.AddHeader(k_HeaderIfNoneMatch, pathCreateOptions.IfNoneMatch); - } - if (!pathCreateOptions.IfModifiedSince.empty()) - { - request.AddHeader(k_HeaderIfModifiedSince, pathCreateOptions.IfModifiedSince); - } - if (!pathCreateOptions.IfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderIfUnmodifiedSince, pathCreateOptions.IfUnmodifiedSince); - } - if (!pathCreateOptions.SourceIfMatch.empty()) - { - request.AddHeader(k_HeaderSourceIfMatch, pathCreateOptions.SourceIfMatch); - } - if (!pathCreateOptions.SourceIfNoneMatch.empty()) - { - request.AddHeader(k_HeaderSourceIfNoneMatch, pathCreateOptions.SourceIfNoneMatch); - } - if (!pathCreateOptions.SourceIfModifiedSince.empty()) - { - request.AddHeader(k_HeaderSourceIfModifiedSince, pathCreateOptions.SourceIfModifiedSince); - } - if (!pathCreateOptions.SourceIfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderSourceIfUnmodifiedSince, pathCreateOptions.SourceIfUnmodifiedSince); - } - return request; - } - - struct PathCreateResponse - { - std::string Date; - std::string ETag; - std::string LastModified; - std::string RequestId; - std::string Version; - std::string Continuation; - uint64_t ContentLength = uint64_t(); - }; - - static PathCreateResponse PathCreateParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Created) - { - // The file or directory was created. - PathCreateResponse result; - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) + if (!createOptions.ContentLanguage.empty()) { - result.Date = response.GetHeaders().at(k_HeaderDate); + request.AddHeader(Details::c_HeaderContentLanguage, createOptions.ContentLanguage); } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) + if (!createOptions.ContentDisposition.empty()) { - result.ETag = response.GetHeaders().at(k_HeaderETag); + request.AddHeader(Details::c_HeaderContentDisposition, createOptions.ContentDisposition); } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) + if (!createOptions.ContentType.empty()) { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); + request.AddHeader(Details::c_HeaderContentType, createOptions.ContentType); } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) + if (!createOptions.RenameSource.empty()) { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); + request.AddHeader(Details::c_HeaderRenameSource, createOptions.RenameSource); } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) + if (!createOptions.LeaseIdOptional.empty()) { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); + request.AddHeader(Details::c_HeaderLeaseIdOptional, createOptions.LeaseIdOptional); } - if (response.GetHeaders().find(k_HeaderXMsContinuation) != response.GetHeaders().end()) + if (!createOptions.SourceLeaseId.empty()) { - result.Continuation = response.GetHeaders().at(k_HeaderXMsContinuation); + request.AddHeader(Details::c_HeaderSourceLeaseId, createOptions.SourceLeaseId); } - if (response.GetHeaders().find(k_HeaderContentLength) != response.GetHeaders().end()) + if (!createOptions.Properties.empty()) { - result.ContentLength = std::stoull(response.GetHeaders().at(k_HeaderContentLength)); + request.AddHeader(Details::c_HeaderProperties, createOptions.Properties); } - return result; + if (!createOptions.Permissions.empty()) + { + request.AddHeader(Details::c_HeaderPermissions, createOptions.Permissions); + } + if (!createOptions.Umask.empty()) + { + request.AddHeader(Details::c_HeaderUmask, createOptions.Umask); + } + if (!createOptions.IfMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfMatch, createOptions.IfMatch); + } + if (!createOptions.IfNoneMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfNoneMatch, createOptions.IfNoneMatch); + } + if (!createOptions.IfModifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfModifiedSince, createOptions.IfModifiedSince); + } + if (!createOptions.IfUnmodifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfUnmodifiedSince, createOptions.IfUnmodifiedSince); + } + if (!createOptions.SourceIfMatch.empty()) + { + request.AddHeader(Details::c_HeaderSourceIfMatch, createOptions.SourceIfMatch); + } + if (!createOptions.SourceIfNoneMatch.empty()) + { + request.AddHeader(Details::c_HeaderSourceIfNoneMatch, createOptions.SourceIfNoneMatch); + } + if (!createOptions.SourceIfModifiedSince.empty()) + { + request.AddHeader( + Details::c_HeaderSourceIfModifiedSince, createOptions.SourceIfModifiedSince); + } + if (!createOptions.SourceIfUnmodifiedSince.empty()) + { + request.AddHeader( + Details::c_HeaderSourceIfUnmodifiedSince, createOptions.SourceIfUnmodifiedSince); + } + return request; } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - static PathCreateResponse PathCreate(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const PathCreateOptions& pathCreateOptions) - { - // TODO: Pipeline will be added when available. - return PathCreateParseResponse(transport->Send(context, PathCreateCreateRequest(std::move(url), pathCreateOptions))); - } + static PathCreateResponse CreateParseResponse( + std::unique_ptr responsePtr) + { + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Created) + { + // The file or directory was created. + PathCreateResponse result; + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsContinuation) + != response.GetHeaders().end()) + { + result.Continuation = response.GetHeaders().at(Details::c_HeaderXMsContinuation); + } + if (response.GetHeaders().find(Details::c_HeaderContentLength) + != response.GetHeaders().end()) + { + result.ContentLength + = std::stoll(response.GetHeaders().at(Details::c_HeaderContentLength)); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } + } - struct PathUpdateOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - PathUpdateAction Action; // The action must be "append" to upload data to be appended to a file, "flush" to flush previously uploaded data to a file, "setProperties" to set the properties of a file or directory, "setAccessControl" to set the owner, group, permissions, or access control list for a file or directory, or "setAccessControlRecursive" to set the access control list for a directory recursively. Note that Hierarchical Namespace must be enabled for the account in order to use access control. Also note that the Access Control List (ACL) includes permissions for the owner, owning group, and others, so the x-ms-permissions and x-ms-acl request headers are mutually exclusive. - uint32_t MaxRecords = uint32_t(); // Optional. Valid for "SetAccessControlRecursive" operation. It specifies the maximum number of files or directories on which the acl change will be applied. If omitted or greater than 2,000, the request will process up to 2,000 items - std::string Continuation; // Optional. The number of paths processed with each invocation is limited. If the number of paths to be processed exceeds this limit, a continuation token is returned in the response header x-ms-continuation. When a continuation token is returned in the response, it must be percent-encoded and specified in a subsequent invocation of setAcessControlRecursive operation. - PathSetAccessControlRecursiveMode Mode; // Mode "set" sets POSIX access control rights on files and directories, "modify" modifies one or more POSIX access control rights that pre-exist on files and directories, "remove" removes one or more POSIX access control rights that were present earlier on files and directories - uint64_t Position = uint64_t(); // This parameter allows the caller to upload data in parallel and control the order in which it is appended to the file. It is required when uploading data to be appended to the file and when flushing previously uploaded data to the file. The value must be the position where the data is to be appended. Uploaded data is not immediately flushed, or written, to the file. To flush, the previously uploaded data must be contiguous, the position parameter must be specified and equal to the length of the file after all data has been written, and there must not be a request entity body included with the request. - bool RetainUncommittedData = bool(); // Valid only for flush operations. If "true", uncommitted data is retained after the flush operation completes; otherwise, the uncommitted data is deleted after the flush operation. The default is false. Data at offsets less than the specified position are written to the file when flush succeeds, but this optional parameter allows data after the flush position to be retained for a future flush operation. - bool Close = bool(); // Azure Storage Events allow applications to receive notifications when files change. When Azure Storage Events are enabled, a file changed event is raised. This event has a property indicating whether this is the final change to distinguish the difference between an intermediate flush to a file stream and the final close of a file stream. The close query parameter is valid only when the action is "flush" and change notifications are enabled. If the value of close is "true" and the flush operation completes successfully, the service raises a file change notification with a property indicating that this is the final update (the file stream has been closed). If "false" a change notification is raised indicating the file has changed. The default is false. This query parameter is set to true by the Hadoop ABFS driver to indicate that the file stream has been closed." - uint64_t ContentLength = uint64_t(); // Required for "Append Data" and "Flush Data". Must be 0 for "Flush Data". Must be the length of the request content in bytes for "Append Data". - std::string ContentMD5; // Specify the transactional md5 for the body, to be validated by the service. - std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's lease is active and matches this ID. - std::string CacheControl; // Optional. Sets the blob's cache control. If specified, this property is stored with the blob and returned with a read request. - std::string ContentType; // Optional. Sets the blob's content type. If specified, this property is stored with the blob and returned with a read request. - std::string ContentDisposition; // Optional. Sets the blob's Content-Disposition header. - std::string ContentEncoding; // Optional. Sets the blob's content encoding. If specified, this property is stored with the blob and returned with a read request. - std::string ContentLanguage; // Optional. Set the blob's content language. If specified, this property is stored with the blob and returned with a read request. - std::string Properties; // Optional. User-defined properties to be stored with the filesystem, in the format of a comma-separated list of name and value pairs "n1=v1, n2=v2, ...", where each value is a base64 encoded string. Note that the string may only contain ASCII characters in the ISO-8859-1 character set. If the filesystem exists, any properties not included in the list will be removed. All properties are removed if the header is omitted. To merge new and existing properties, first get all existing properties and the current E-Tag, then make a conditional request with the E-Tag and include values for all properties. - std::string Owner; // Optional. The owner of the blob or directory. - std::string Group; // Optional. The owning group of the blob or directory. - std::string Permissions; // Optional and only valid if Hierarchical Namespace is enabled for the account. Sets POSIX access permissions for the file owner, the file owning group, and others. Each class may be granted read, write, or execute permission. The sticky bit is also supported. Both symbolic (rwxrw-rw-) and 4-digit octal notation (e.g. 0766) are supported. - std::string Acl; // Sets POSIX access control rights on files and directories. The value is a comma-separated list of access control entries. Each access control entry (ACE) consists of a scope, a type, a user or group identifier, and permissions in the format "[scope:][type]:[id]:[permissions]". - std::string IfMatch; // Specify an ETag value to operate only on blobs with a matching value. - std::string IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. - std::string IfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - std::vector Body; // Initial data - }; + static Azure::Core::Http::Request UpdateCreateRequest( + std::string url, + const UpdateOptions& updateOptions) + { + Azure::Core::Http::Request request( + Azure::Core::Http::HttpMethod::Patch, std::move(url), updateOptions.Body); + if (!updateOptions.ClientRequestId.empty()) + { + request.AddHeader(Details::c_HeaderClientRequestId, updateOptions.ClientRequestId); + } + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter(Details::c_QueryTimeout, std::to_string(updateOptions.Timeout)); + request.AddHeader(Details::c_HeaderApiVersionParameter, updateOptions.ApiVersionParameter); + request.AddQueryParameter( + Details::c_QueryPathUpdateAction, PathUpdateActionToString(updateOptions.Action)); - static Azure::Core::Http::Request PathUpdateCreateRequest(std::string url, const PathUpdateOptions& pathUpdateOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Patch, std::move(url), pathUpdateOptions.Body); - if (!pathUpdateOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, pathUpdateOptions.ClientRequestId); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(pathUpdateOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, pathUpdateOptions.ApiVersionParameter); - request.AddQueryParameter(k_QueryPathUpdateAction, PathUpdateActionToString(pathUpdateOptions.Action)); + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryMaxRecords, std::to_string(updateOptions.MaxRecords)); + if (!updateOptions.Continuation.empty()) + { + request.AddQueryParameter(Details::c_QueryContinuation, updateOptions.Continuation); + } + request.AddQueryParameter( + Details::c_QueryPathSetAccessControlRecursiveMode, + PathSetAccessControlRecursiveModeToString(updateOptions.Mode)); - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryMaxRecords, std::to_string(pathUpdateOptions.MaxRecords)); - if (!pathUpdateOptions.Continuation.empty()) - { - request.AddQueryParameter(k_QueryContinuation, pathUpdateOptions.Continuation); - } - request.AddQueryParameter(k_QueryPathSetAccessControlRecursiveMode, PathSetAccessControlRecursiveModeToString(pathUpdateOptions.Mode)); + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter(Details::c_QueryPosition, std::to_string(updateOptions.Position)); - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryPosition, std::to_string(pathUpdateOptions.Position)); + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryRetainUncommittedData, + (updateOptions.RetainUncommittedData ? "true" : "false")); - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryRetainUncommittedData, (pathUpdateOptions.RetainUncommittedData ? "true" : "false")); + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter(Details::c_QueryClose, (updateOptions.Close ? "true" : "false")); - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryClose, (pathUpdateOptions.Close ? "true" : "false")); + // TODO: Need to check for Null when Nullable is ready + request.AddHeader( + Details::c_HeaderContentLength, std::to_string(updateOptions.ContentLength)); + if (!updateOptions.ContentMD5.empty()) + { + request.AddHeader(Details::c_HeaderContentMD5, updateOptions.ContentMD5); + } + if (!updateOptions.LeaseIdOptional.empty()) + { + request.AddHeader(Details::c_HeaderLeaseIdOptional, updateOptions.LeaseIdOptional); + } + if (!updateOptions.CacheControl.empty()) + { + request.AddHeader(Details::c_HeaderCacheControl, updateOptions.CacheControl); + } + if (!updateOptions.ContentType.empty()) + { + request.AddHeader(Details::c_HeaderContentType, updateOptions.ContentType); + } + if (!updateOptions.ContentDisposition.empty()) + { + request.AddHeader(Details::c_HeaderContentDisposition, updateOptions.ContentDisposition); + } + if (!updateOptions.ContentEncoding.empty()) + { + request.AddHeader(Details::c_HeaderContentEncoding, updateOptions.ContentEncoding); + } + if (!updateOptions.ContentLanguage.empty()) + { + request.AddHeader(Details::c_HeaderContentLanguage, updateOptions.ContentLanguage); + } + if (!updateOptions.Properties.empty()) + { + request.AddHeader(Details::c_HeaderProperties, updateOptions.Properties); + } + if (!updateOptions.Owner.empty()) + { + request.AddHeader(Details::c_HeaderOwner, updateOptions.Owner); + } + if (!updateOptions.Group.empty()) + { + request.AddHeader(Details::c_HeaderGroup, updateOptions.Group); + } + if (!updateOptions.Permissions.empty()) + { + request.AddHeader(Details::c_HeaderPermissions, updateOptions.Permissions); + } + if (!updateOptions.Acl.empty()) + { + request.AddHeader(Details::c_HeaderAcl, updateOptions.Acl); + } + if (!updateOptions.IfMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfMatch, updateOptions.IfMatch); + } + if (!updateOptions.IfNoneMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfNoneMatch, updateOptions.IfNoneMatch); + } + if (!updateOptions.IfModifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfModifiedSince, updateOptions.IfModifiedSince); + } + if (!updateOptions.IfUnmodifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfUnmodifiedSince, updateOptions.IfUnmodifiedSince); + } + return request; + } - // TODO: We should enforce null check here when Optional is ready. - request.AddHeader(k_HeaderContentLength, std::to_string(pathUpdateOptions.ContentLength)); - if (!pathUpdateOptions.ContentMD5.empty()) + static PathUpdateResponse UpdateParseResponse( + std::unique_ptr responsePtr) { - request.AddHeader(k_HeaderContentMD5, pathUpdateOptions.ContentMD5); + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + { + // The data was flushed (written) to the file or the properties were set successfully. + // Response body is optional and is valid only for "SetAccessControlRecursive" + PathUpdateResponse result + = PathUpdateResponse::PathUpdateResponseFromSetAccessControlRecursiveResponse( + SetAccessControlRecursiveResponse::CreateFromJson(nlohmann::json::parse( + *Azure::Core::Http::Response::ConstructBodyBufferFromStream( + response.GetBodyStream())))); + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderAcceptRanges) + != response.GetHeaders().end()) + { + result.AcceptRanges = response.GetHeaders().at(Details::c_HeaderAcceptRanges); + } + if (response.GetHeaders().find(Details::c_HeaderCacheControl) + != response.GetHeaders().end()) + { + result.CacheControl = response.GetHeaders().at(Details::c_HeaderCacheControl); + } + if (response.GetHeaders().find(Details::c_HeaderContentDisposition) + != response.GetHeaders().end()) + { + result.ContentDisposition + = response.GetHeaders().at(Details::c_HeaderContentDisposition); + } + if (response.GetHeaders().find(Details::c_HeaderContentEncoding) + != response.GetHeaders().end()) + { + result.ContentEncoding = response.GetHeaders().at(Details::c_HeaderContentEncoding); + } + if (response.GetHeaders().find(Details::c_HeaderContentLanguage) + != response.GetHeaders().end()) + { + result.ContentLanguage = response.GetHeaders().at(Details::c_HeaderContentLanguage); + } + if (response.GetHeaders().find(Details::c_HeaderContentLength) + != response.GetHeaders().end()) + { + result.ContentLength + = std::stoll(response.GetHeaders().at(Details::c_HeaderContentLength)); + } + if (response.GetHeaders().find(Details::c_HeaderContentRange) + != response.GetHeaders().end()) + { + result.ContentRange = response.GetHeaders().at(Details::c_HeaderContentRange); + } + if (response.GetHeaders().find(Details::c_HeaderContentType) + != response.GetHeaders().end()) + { + result.ContentType = response.GetHeaders().at(Details::c_HeaderContentType); + } + if (response.GetHeaders().find(Details::c_HeaderContentMD5) + != response.GetHeaders().end()) + { + result.ContentMD5 = response.GetHeaders().at(Details::c_HeaderContentMD5); + } + if (response.GetHeaders().find(Details::c_HeaderXMsProperties) + != response.GetHeaders().end()) + { + result.Properties = response.GetHeaders().at(Details::c_HeaderXMsProperties); + } + if (response.GetHeaders().find(Details::c_HeaderXMsContinuation) + != response.GetHeaders().end()) + { + result.XMsContinuation = response.GetHeaders().at(Details::c_HeaderXMsContinuation); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + return result; + } + else if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Accepted) + { + // The uploaded data was accepted. + PathUpdateResponse result; + if (response.GetHeaders().find(Details::c_HeaderContentMD5) + != response.GetHeaders().end()) + { + result.ContentMD5 = response.GetHeaders().at(Details::c_HeaderContentMD5); + } + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } } - if (!pathUpdateOptions.LeaseIdOptional.empty()) - { - request.AddHeader(k_HeaderLeaseIdOptional, pathUpdateOptions.LeaseIdOptional); - } - if (!pathUpdateOptions.CacheControl.empty()) - { - request.AddHeader(k_HeaderCacheControl, pathUpdateOptions.CacheControl); - } - if (!pathUpdateOptions.ContentType.empty()) - { - request.AddHeader(k_HeaderContentType, pathUpdateOptions.ContentType); - } - if (!pathUpdateOptions.ContentDisposition.empty()) - { - request.AddHeader(k_HeaderContentDisposition, pathUpdateOptions.ContentDisposition); - } - if (!pathUpdateOptions.ContentEncoding.empty()) - { - request.AddHeader(k_HeaderContentEncoding, pathUpdateOptions.ContentEncoding); - } - if (!pathUpdateOptions.ContentLanguage.empty()) - { - request.AddHeader(k_HeaderContentLanguage, pathUpdateOptions.ContentLanguage); - } - if (!pathUpdateOptions.Properties.empty()) - { - request.AddHeader(k_HeaderProperties, pathUpdateOptions.Properties); - } - if (!pathUpdateOptions.Owner.empty()) - { - request.AddHeader(k_HeaderOwner, pathUpdateOptions.Owner); - } - if (!pathUpdateOptions.Group.empty()) - { - request.AddHeader(k_HeaderGroup, pathUpdateOptions.Group); - } - if (!pathUpdateOptions.Permissions.empty()) - { - request.AddHeader(k_HeaderPermissions, pathUpdateOptions.Permissions); - } - if (!pathUpdateOptions.Acl.empty()) - { - request.AddHeader(k_HeaderAcl, pathUpdateOptions.Acl); - } - if (!pathUpdateOptions.IfMatch.empty()) - { - request.AddHeader(k_HeaderIfMatch, pathUpdateOptions.IfMatch); - } - if (!pathUpdateOptions.IfNoneMatch.empty()) - { - request.AddHeader(k_HeaderIfNoneMatch, pathUpdateOptions.IfNoneMatch); - } - if (!pathUpdateOptions.IfModifiedSince.empty()) - { - request.AddHeader(k_HeaderIfModifiedSince, pathUpdateOptions.IfModifiedSince); - } - if (!pathUpdateOptions.IfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderIfUnmodifiedSince, pathUpdateOptions.IfUnmodifiedSince); - } - return request; - } - struct PathUpdateResponse - { - std::string Date; - std::string ETag; - std::string LastModified; - std::string AcceptRanges; - std::string CacheControl; - std::string ContentDisposition; - std::string ContentEncoding; - std::string ContentLanguage; - uint64_t ContentLength = uint64_t(); - std::string ContentRange; - std::string ContentType; - std::string ContentMD5; - std::string Properties; - std::string XMsContinuation; - std::string RequestId; - std::string Version; - uint32_t DirectoriesSuccessful = uint32_t(); - uint32_t FilesSuccessful = uint32_t(); - uint32_t FailureCount = uint32_t(); - std::vector FailedEntries; - - static PathUpdateResponse PathUpdateResponseFromSetAccessControlRecursiveResponse(SetAccessControlRecursiveResponse object) + static Azure::Core::Http::Request LeaseCreateRequest( + std::string url, + const LeaseOptions& leaseOptions) { - PathUpdateResponse result; - result.DirectoriesSuccessful = object.DirectoriesSuccessful; - result.FilesSuccessful = object.FilesSuccessful; - result.FailureCount = object.FailureCount; - result.FailedEntries = std::move(object.FailedEntries); + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Post, url); + if (!leaseOptions.ClientRequestId.empty()) + { + request.AddHeader(Details::c_HeaderClientRequestId, leaseOptions.ClientRequestId); + } + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter(Details::c_QueryTimeout, std::to_string(leaseOptions.Timeout)); + request.AddHeader(Details::c_HeaderApiVersionParameter, leaseOptions.ApiVersionParameter); + request.AddHeader( + Details::c_HeaderPathLeaseAction, PathLeaseActionToString(leaseOptions.XMsLeaseAction)); - return result; + // TODO: Need to check for Null when Nullable is ready + request.AddHeader( + Details::c_HeaderXMsLeaseDuration, std::to_string(leaseOptions.XMsLeaseDuration)); + + // TODO: Need to check for Null when Nullable is ready + request.AddHeader( + Details::c_HeaderXMsLeaseBreakPeriod, std::to_string(leaseOptions.XMsLeaseBreakPeriod)); + if (!leaseOptions.LeaseIdOptional.empty()) + { + request.AddHeader(Details::c_HeaderLeaseIdOptional, leaseOptions.LeaseIdOptional); + } + if (!leaseOptions.ProposedLeaseIdOptional.empty()) + { + request.AddHeader( + Details::c_HeaderProposedLeaseIdOptional, leaseOptions.ProposedLeaseIdOptional); + } + if (!leaseOptions.IfMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfMatch, leaseOptions.IfMatch); + } + if (!leaseOptions.IfNoneMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfNoneMatch, leaseOptions.IfNoneMatch); + } + if (!leaseOptions.IfModifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfModifiedSince, leaseOptions.IfModifiedSince); + } + if (!leaseOptions.IfUnmodifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfUnmodifiedSince, leaseOptions.IfUnmodifiedSince); + } + return request; + } + + static PathLeaseResponse LeaseParseResponse( + std::unique_ptr responsePtr) + { + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + { + // The "renew", "change" or "release" action was successful. + PathLeaseResponse result; + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseId) + != response.GetHeaders().end()) + { + result.LeaseId = response.GetHeaders().at(Details::c_HeaderXMsLeaseId); + } + return result; + } + else if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Created) + { + // A new lease has been created. The "acquire" action was successful. + PathLeaseResponse result; + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseId) + != response.GetHeaders().end()) + { + result.LeaseId = response.GetHeaders().at(Details::c_HeaderXMsLeaseId); + } + return result; + } + else if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Accepted) + { + // The "break" lease action was successful. + PathLeaseResponse result; + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseTime) + != response.GetHeaders().end()) + { + result.LeaseTime = response.GetHeaders().at(Details::c_HeaderXMsLeaseTime); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } + } + + static Azure::Core::Http::Request ReadCreateRequest( + std::string url, + const ReadOptions& readOptions) + { + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Get, url); + if (!readOptions.ClientRequestId.empty()) + { + request.AddHeader(Details::c_HeaderClientRequestId, readOptions.ClientRequestId); + } + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter(Details::c_QueryTimeout, std::to_string(readOptions.Timeout)); + request.AddHeader(Details::c_HeaderApiVersionParameter, readOptions.ApiVersionParameter); + if (!readOptions.Range.empty()) + { + request.AddHeader(Details::c_HeaderRange, readOptions.Range); + } + if (!readOptions.LeaseIdOptional.empty()) + { + request.AddHeader(Details::c_HeaderLeaseIdOptional, readOptions.LeaseIdOptional); + } + // TODO: Need to check for Null when Nullable is ready + request.AddHeader( + Details::c_HeaderXMsRangeGetContentMd5, + (readOptions.XMsRangeGetContentMd5 ? "true" : "false")); + if (!readOptions.IfMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfMatch, readOptions.IfMatch); + } + if (!readOptions.IfNoneMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfNoneMatch, readOptions.IfNoneMatch); + } + if (!readOptions.IfModifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfModifiedSince, readOptions.IfModifiedSince); + } + if (!readOptions.IfUnmodifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfUnmodifiedSince, readOptions.IfUnmodifiedSince); + } + return request; + } + + static PathReadResponse ReadParseResponse( + std::unique_ptr responsePtr) + { + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + { + // Ok + PathReadResponse result; + result.BodyStream = response.GetBodyStream(); + if (response.GetHeaders().find(Details::c_HeaderAcceptRanges) + != response.GetHeaders().end()) + { + result.AcceptRanges = response.GetHeaders().at(Details::c_HeaderAcceptRanges); + } + if (response.GetHeaders().find(Details::c_HeaderCacheControl) + != response.GetHeaders().end()) + { + result.CacheControl = response.GetHeaders().at(Details::c_HeaderCacheControl); + } + if (response.GetHeaders().find(Details::c_HeaderContentDisposition) + != response.GetHeaders().end()) + { + result.ContentDisposition + = response.GetHeaders().at(Details::c_HeaderContentDisposition); + } + if (response.GetHeaders().find(Details::c_HeaderContentEncoding) + != response.GetHeaders().end()) + { + result.ContentEncoding = response.GetHeaders().at(Details::c_HeaderContentEncoding); + } + if (response.GetHeaders().find(Details::c_HeaderContentLanguage) + != response.GetHeaders().end()) + { + result.ContentLanguage = response.GetHeaders().at(Details::c_HeaderContentLanguage); + } + if (response.GetHeaders().find(Details::c_HeaderContentLength) + != response.GetHeaders().end()) + { + result.ContentLength + = std::stoll(response.GetHeaders().at(Details::c_HeaderContentLength)); + } + if (response.GetHeaders().find(Details::c_HeaderContentRange) + != response.GetHeaders().end()) + { + result.ContentRange = response.GetHeaders().at(Details::c_HeaderContentRange); + } + if (response.GetHeaders().find(Details::c_HeaderContentType) + != response.GetHeaders().end()) + { + result.ContentType = response.GetHeaders().at(Details::c_HeaderContentType); + } + if (response.GetHeaders().find(Details::c_HeaderContentMD5) + != response.GetHeaders().end()) + { + result.ContentMD5 = response.GetHeaders().at(Details::c_HeaderContentMD5); + } + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsResourceType) + != response.GetHeaders().end()) + { + result.ResourceType = response.GetHeaders().at(Details::c_HeaderXMsResourceType); + } + if (response.GetHeaders().find(Details::c_HeaderXMsProperties) + != response.GetHeaders().end()) + { + result.Properties = response.GetHeaders().at(Details::c_HeaderXMsProperties); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseDuration) + != response.GetHeaders().end()) + { + result.LeaseDuration = response.GetHeaders().at(Details::c_HeaderXMsLeaseDuration); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseState) + != response.GetHeaders().end()) + { + result.LeaseState = response.GetHeaders().at(Details::c_HeaderXMsLeaseState); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseStatus) + != response.GetHeaders().end()) + { + result.LeaseStatus = response.GetHeaders().at(Details::c_HeaderXMsLeaseStatus); + } + return result; + } + else if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::PartialContent) + { + // Partial content + PathReadResponse result; + result.BodyStream = response.GetBodyStream(); + if (response.GetHeaders().find(Details::c_HeaderAcceptRanges) + != response.GetHeaders().end()) + { + result.AcceptRanges = response.GetHeaders().at(Details::c_HeaderAcceptRanges); + } + if (response.GetHeaders().find(Details::c_HeaderCacheControl) + != response.GetHeaders().end()) + { + result.CacheControl = response.GetHeaders().at(Details::c_HeaderCacheControl); + } + if (response.GetHeaders().find(Details::c_HeaderContentDisposition) + != response.GetHeaders().end()) + { + result.ContentDisposition + = response.GetHeaders().at(Details::c_HeaderContentDisposition); + } + if (response.GetHeaders().find(Details::c_HeaderContentEncoding) + != response.GetHeaders().end()) + { + result.ContentEncoding = response.GetHeaders().at(Details::c_HeaderContentEncoding); + } + if (response.GetHeaders().find(Details::c_HeaderContentLanguage) + != response.GetHeaders().end()) + { + result.ContentLanguage = response.GetHeaders().at(Details::c_HeaderContentLanguage); + } + if (response.GetHeaders().find(Details::c_HeaderContentLength) + != response.GetHeaders().end()) + { + result.ContentLength + = std::stoll(response.GetHeaders().at(Details::c_HeaderContentLength)); + } + if (response.GetHeaders().find(Details::c_HeaderContentRange) + != response.GetHeaders().end()) + { + result.ContentRange = response.GetHeaders().at(Details::c_HeaderContentRange); + } + if (response.GetHeaders().find(Details::c_HeaderContentType) + != response.GetHeaders().end()) + { + result.ContentType = response.GetHeaders().at(Details::c_HeaderContentType); + } + if (response.GetHeaders().find(Details::c_HeaderContentMD5) + != response.GetHeaders().end()) + { + result.ContentMD5 = response.GetHeaders().at(Details::c_HeaderContentMD5); + } + if (response.GetHeaders().find(Details::c_HeaderXMsContentMd5) + != response.GetHeaders().end()) + { + result.XMsContentMd5 = response.GetHeaders().at(Details::c_HeaderXMsContentMd5); + } + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsResourceType) + != response.GetHeaders().end()) + { + result.ResourceType = response.GetHeaders().at(Details::c_HeaderXMsResourceType); + } + if (response.GetHeaders().find(Details::c_HeaderXMsProperties) + != response.GetHeaders().end()) + { + result.Properties = response.GetHeaders().at(Details::c_HeaderXMsProperties); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseDuration) + != response.GetHeaders().end()) + { + result.LeaseDuration = response.GetHeaders().at(Details::c_HeaderXMsLeaseDuration); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseState) + != response.GetHeaders().end()) + { + result.LeaseState = response.GetHeaders().at(Details::c_HeaderXMsLeaseState); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseStatus) + != response.GetHeaders().end()) + { + result.LeaseStatus = response.GetHeaders().at(Details::c_HeaderXMsLeaseStatus); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } + } + + static Azure::Core::Http::Request GetPropertiesCreateRequest( + std::string url, + const GetPropertiesOptions& getPropertiesOptions) + { + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Head, url); + if (!getPropertiesOptions.ClientRequestId.empty()) + { + request.AddHeader(Details::c_HeaderClientRequestId, getPropertiesOptions.ClientRequestId); + } + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryTimeout, std::to_string(getPropertiesOptions.Timeout)); + request.AddHeader( + Details::c_HeaderApiVersionParameter, getPropertiesOptions.ApiVersionParameter); + if (getPropertiesOptions.Action != PathGetPropertiesAction::Unknown) + { + request.AddQueryParameter( + Details::c_QueryPathGetPropertiesAction, + PathGetPropertiesActionToString(getPropertiesOptions.Action)); + } + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryUpn, (getPropertiesOptions.Upn ? "true" : "false")); + if (!getPropertiesOptions.LeaseIdOptional.empty()) + { + request.AddHeader(Details::c_HeaderLeaseIdOptional, getPropertiesOptions.LeaseIdOptional); + } + if (!getPropertiesOptions.IfMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfMatch, getPropertiesOptions.IfMatch); + } + if (!getPropertiesOptions.IfNoneMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfNoneMatch, getPropertiesOptions.IfNoneMatch); + } + if (!getPropertiesOptions.IfModifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfModifiedSince, getPropertiesOptions.IfModifiedSince); + } + if (!getPropertiesOptions.IfUnmodifiedSince.empty()) + { + request.AddHeader( + Details::c_HeaderIfUnmodifiedSince, getPropertiesOptions.IfUnmodifiedSince); + } + return request; + } + + static PathGetPropertiesResponse GetPropertiesParseResponse( + std::unique_ptr responsePtr) + { + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + { + // Returns all properties for the file or directory. + PathGetPropertiesResponse result; + if (response.GetHeaders().find(Details::c_HeaderAcceptRanges) + != response.GetHeaders().end()) + { + result.AcceptRanges = response.GetHeaders().at(Details::c_HeaderAcceptRanges); + } + if (response.GetHeaders().find(Details::c_HeaderCacheControl) + != response.GetHeaders().end()) + { + result.CacheControl = response.GetHeaders().at(Details::c_HeaderCacheControl); + } + if (response.GetHeaders().find(Details::c_HeaderContentDisposition) + != response.GetHeaders().end()) + { + result.ContentDisposition + = response.GetHeaders().at(Details::c_HeaderContentDisposition); + } + if (response.GetHeaders().find(Details::c_HeaderContentEncoding) + != response.GetHeaders().end()) + { + result.ContentEncoding = response.GetHeaders().at(Details::c_HeaderContentEncoding); + } + if (response.GetHeaders().find(Details::c_HeaderContentLanguage) + != response.GetHeaders().end()) + { + result.ContentLanguage = response.GetHeaders().at(Details::c_HeaderContentLanguage); + } + if (response.GetHeaders().find(Details::c_HeaderContentLength) + != response.GetHeaders().end()) + { + result.ContentLength + = std::stoll(response.GetHeaders().at(Details::c_HeaderContentLength)); + } + if (response.GetHeaders().find(Details::c_HeaderContentRange) + != response.GetHeaders().end()) + { + result.ContentRange = response.GetHeaders().at(Details::c_HeaderContentRange); + } + if (response.GetHeaders().find(Details::c_HeaderContentType) + != response.GetHeaders().end()) + { + result.ContentType = response.GetHeaders().at(Details::c_HeaderContentType); + } + if (response.GetHeaders().find(Details::c_HeaderContentMD5) + != response.GetHeaders().end()) + { + result.ContentMD5 = response.GetHeaders().at(Details::c_HeaderContentMD5); + } + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsResourceType) + != response.GetHeaders().end()) + { + result.ResourceType = response.GetHeaders().at(Details::c_HeaderXMsResourceType); + } + if (response.GetHeaders().find(Details::c_HeaderXMsProperties) + != response.GetHeaders().end()) + { + result.Properties = response.GetHeaders().at(Details::c_HeaderXMsProperties); + } + if (response.GetHeaders().find(Details::c_HeaderXMsOwner) != response.GetHeaders().end()) + { + result.Owner = response.GetHeaders().at(Details::c_HeaderXMsOwner); + } + if (response.GetHeaders().find(Details::c_HeaderXMsGroup) != response.GetHeaders().end()) + { + result.Group = response.GetHeaders().at(Details::c_HeaderXMsGroup); + } + if (response.GetHeaders().find(Details::c_HeaderXMsPermissions) + != response.GetHeaders().end()) + { + result.Permissions = response.GetHeaders().at(Details::c_HeaderXMsPermissions); + } + if (response.GetHeaders().find(Details::c_HeaderXMsAcl) != response.GetHeaders().end()) + { + result.ACL = response.GetHeaders().at(Details::c_HeaderXMsAcl); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseDuration) + != response.GetHeaders().end()) + { + result.LeaseDuration = response.GetHeaders().at(Details::c_HeaderXMsLeaseDuration); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseState) + != response.GetHeaders().end()) + { + result.LeaseState = response.GetHeaders().at(Details::c_HeaderXMsLeaseState); + } + if (response.GetHeaders().find(Details::c_HeaderXMsLeaseStatus) + != response.GetHeaders().end()) + { + result.LeaseStatus = response.GetHeaders().at(Details::c_HeaderXMsLeaseStatus); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } + } + + static Azure::Core::Http::Request DeleteCreateRequest( + std::string url, + const DeleteOptions& deleteOptions) + { + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Delete, url); + if (!deleteOptions.ClientRequestId.empty()) + { + request.AddHeader(Details::c_HeaderClientRequestId, deleteOptions.ClientRequestId); + } + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter(Details::c_QueryTimeout, std::to_string(deleteOptions.Timeout)); + request.AddHeader(Details::c_HeaderApiVersionParameter, deleteOptions.ApiVersionParameter); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryRecursiveOptional, + (deleteOptions.RecursiveOptional ? "true" : "false")); + if (!deleteOptions.Continuation.empty()) + { + request.AddQueryParameter(Details::c_QueryContinuation, deleteOptions.Continuation); + } + if (!deleteOptions.LeaseIdOptional.empty()) + { + request.AddHeader(Details::c_HeaderLeaseIdOptional, deleteOptions.LeaseIdOptional); + } + if (!deleteOptions.IfMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfMatch, deleteOptions.IfMatch); + } + if (!deleteOptions.IfNoneMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfNoneMatch, deleteOptions.IfNoneMatch); + } + if (!deleteOptions.IfModifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfModifiedSince, deleteOptions.IfModifiedSince); + } + if (!deleteOptions.IfUnmodifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfUnmodifiedSince, deleteOptions.IfUnmodifiedSince); + } + return request; + } + + static PathDeleteResponse DeleteParseResponse( + std::unique_ptr responsePtr) + { + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + { + // The file was deleted. + PathDeleteResponse result; + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + if (response.GetHeaders().find(Details::c_HeaderXMsContinuation) + != response.GetHeaders().end()) + { + result.Continuation = response.GetHeaders().at(Details::c_HeaderXMsContinuation); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } + } + + static Azure::Core::Http::Request SetAccessControlCreateRequest( + std::string url, + const SetAccessControlOptions& setAccessControlOptions) + { + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Patch, url); + request.AddQueryParameter(Details::c_QueryAction, "setAccessControl"); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryTimeout, std::to_string(setAccessControlOptions.Timeout)); + if (!setAccessControlOptions.LeaseIdOptional.empty()) + { + request.AddHeader( + Details::c_HeaderLeaseIdOptional, setAccessControlOptions.LeaseIdOptional); + } + if (!setAccessControlOptions.Owner.empty()) + { + request.AddHeader(Details::c_HeaderOwner, setAccessControlOptions.Owner); + } + if (!setAccessControlOptions.Group.empty()) + { + request.AddHeader(Details::c_HeaderGroup, setAccessControlOptions.Group); + } + if (!setAccessControlOptions.Permissions.empty()) + { + request.AddHeader(Details::c_HeaderPermissions, setAccessControlOptions.Permissions); + } + if (!setAccessControlOptions.Acl.empty()) + { + request.AddHeader(Details::c_HeaderAcl, setAccessControlOptions.Acl); + } + if (!setAccessControlOptions.IfMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfMatch, setAccessControlOptions.IfMatch); + } + if (!setAccessControlOptions.IfNoneMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfNoneMatch, setAccessControlOptions.IfNoneMatch); + } + if (!setAccessControlOptions.IfModifiedSince.empty()) + { + request.AddHeader( + Details::c_HeaderIfModifiedSince, setAccessControlOptions.IfModifiedSince); + } + if (!setAccessControlOptions.IfUnmodifiedSince.empty()) + { + request.AddHeader( + Details::c_HeaderIfUnmodifiedSince, setAccessControlOptions.IfUnmodifiedSince); + } + if (!setAccessControlOptions.ClientRequestId.empty()) + { + request.AddHeader( + Details::c_HeaderClientRequestId, setAccessControlOptions.ClientRequestId); + } + request.AddHeader( + Details::c_HeaderApiVersionParameter, setAccessControlOptions.ApiVersionParameter); + return request; + } + + static PathSetAccessControlResponse SetAccessControlParseResponse( + std::unique_ptr responsePtr) + { + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + { + // Set directory access control response. + PathSetAccessControlResponse result; + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderXMsClientRequestId) + != response.GetHeaders().end()) + { + result.ClientRequestId = response.GetHeaders().at(Details::c_HeaderXMsClientRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } + } + + static Azure::Core::Http::Request SetAccessControlRecursiveCreateRequest( + std::string url, + const SetAccessControlRecursiveOptions& setAccessControlRecursiveOptions) + { + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Patch, url); + request.AddQueryParameter(Details::c_QueryAction, "setAccessControlRecursive"); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryTimeout, std::to_string(setAccessControlRecursiveOptions.Timeout)); + if (!setAccessControlRecursiveOptions.Continuation.empty()) + { + request.AddQueryParameter( + Details::c_QueryContinuation, setAccessControlRecursiveOptions.Continuation); + } + request.AddQueryParameter( + Details::c_QueryPathSetAccessControlRecursiveMode, + PathSetAccessControlRecursiveModeToString(setAccessControlRecursiveOptions.Mode)); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryMaxRecords, + std::to_string(setAccessControlRecursiveOptions.MaxRecords)); + if (!setAccessControlRecursiveOptions.Acl.empty()) + { + request.AddHeader(Details::c_HeaderAcl, setAccessControlRecursiveOptions.Acl); + } + if (!setAccessControlRecursiveOptions.ClientRequestId.empty()) + { + request.AddHeader( + Details::c_HeaderClientRequestId, setAccessControlRecursiveOptions.ClientRequestId); + } + request.AddHeader( + Details::c_HeaderApiVersionParameter, + setAccessControlRecursiveOptions.ApiVersionParameter); + return request; + } + + static PathSetAccessControlRecursiveResponse SetAccessControlRecursiveParseResponse( + std::unique_ptr responsePtr) + { + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + { + // Set directory access control recursive response. + PathSetAccessControlRecursiveResponse result = PathSetAccessControlRecursiveResponse:: + PathSetAccessControlRecursiveResponseFromSetAccessControlRecursiveResponse( + SetAccessControlRecursiveResponse::CreateFromJson(nlohmann::json::parse( + *Azure::Core::Http::Response::ConstructBodyBufferFromStream( + response.GetBodyStream())))); + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderXMsClientRequestId) + != response.GetHeaders().end()) + { + result.ClientRequestId = response.GetHeaders().at(Details::c_HeaderXMsClientRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsContinuation) + != response.GetHeaders().end()) + { + result.Continuation = response.GetHeaders().at(Details::c_HeaderXMsContinuation); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } + } + + static Azure::Core::Http::Request FlushDataCreateRequest( + std::string url, + const FlushDataOptions& flushDataOptions) + { + Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Patch, url); + request.AddQueryParameter(Details::c_QueryAction, "flush"); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryTimeout, std::to_string(flushDataOptions.Timeout)); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryPosition, std::to_string(flushDataOptions.Position)); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryRetainUncommittedData, + (flushDataOptions.RetainUncommittedData ? "true" : "false")); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryClose, (flushDataOptions.Close ? "true" : "false")); + + // TODO: Need to check for Null when Nullable is ready + request.AddHeader( + Details::c_HeaderContentLength, std::to_string(flushDataOptions.ContentLength)); + if (!flushDataOptions.ContentMD5.empty()) + { + request.AddHeader(Details::c_HeaderContentMD5, flushDataOptions.ContentMD5); + } + if (!flushDataOptions.LeaseIdOptional.empty()) + { + request.AddHeader(Details::c_HeaderLeaseIdOptional, flushDataOptions.LeaseIdOptional); + } + if (!flushDataOptions.CacheControl.empty()) + { + request.AddHeader(Details::c_HeaderCacheControl, flushDataOptions.CacheControl); + } + if (!flushDataOptions.ContentType.empty()) + { + request.AddHeader(Details::c_HeaderContentType, flushDataOptions.ContentType); + } + if (!flushDataOptions.ContentDisposition.empty()) + { + request.AddHeader( + Details::c_HeaderContentDisposition, flushDataOptions.ContentDisposition); + } + if (!flushDataOptions.ContentEncoding.empty()) + { + request.AddHeader(Details::c_HeaderContentEncoding, flushDataOptions.ContentEncoding); + } + if (!flushDataOptions.ContentLanguage.empty()) + { + request.AddHeader(Details::c_HeaderContentLanguage, flushDataOptions.ContentLanguage); + } + if (!flushDataOptions.IfMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfMatch, flushDataOptions.IfMatch); + } + if (!flushDataOptions.IfNoneMatch.empty()) + { + request.AddHeader(Details::c_HeaderIfNoneMatch, flushDataOptions.IfNoneMatch); + } + if (!flushDataOptions.IfModifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfModifiedSince, flushDataOptions.IfModifiedSince); + } + if (!flushDataOptions.IfUnmodifiedSince.empty()) + { + request.AddHeader(Details::c_HeaderIfUnmodifiedSince, flushDataOptions.IfUnmodifiedSince); + } + if (!flushDataOptions.ClientRequestId.empty()) + { + request.AddHeader(Details::c_HeaderClientRequestId, flushDataOptions.ClientRequestId); + } + request.AddHeader( + Details::c_HeaderApiVersionParameter, flushDataOptions.ApiVersionParameter); + return request; + } + + static PathFlushDataResponse FlushDataParseResponse( + std::unique_ptr responsePtr) + { + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) + { + // The data was flushed (written) to the file successfully. + PathFlushDataResponse result; + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end()) + { + result.ETag = response.GetHeaders().at(Details::c_HeaderETag); + } + if (response.GetHeaders().find(Details::c_HeaderLastModified) + != response.GetHeaders().end()) + { + result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified); + } + if (response.GetHeaders().find(Details::c_HeaderContentLength) + != response.GetHeaders().end()) + { + result.ContentLength + = std::stoll(response.GetHeaders().at(Details::c_HeaderContentLength)); + } + if (response.GetHeaders().find(Details::c_HeaderXMsClientRequestId) + != response.GetHeaders().end()) + { + result.ClientRequestId = response.GetHeaders().at(Details::c_HeaderXMsClientRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } + } + + static Azure::Core::Http::Request AppendDataCreateRequest( + std::string url, + const AppendDataOptions& appendDataOptions) + { + Azure::Core::Http::Request request( + Azure::Core::Http::HttpMethod::Patch, std::move(url), appendDataOptions.Body); + request.AddQueryParameter(Details::c_QueryAction, "append"); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryPosition, std::to_string(appendDataOptions.Position)); + + // TODO: Need to check for Null when Nullable is ready + request.AddQueryParameter( + Details::c_QueryTimeout, std::to_string(appendDataOptions.Timeout)); + + // TODO: Need to check for Null when Nullable is ready + request.AddHeader( + Details::c_HeaderContentLength, std::to_string(appendDataOptions.ContentLength)); + if (!appendDataOptions.TransactionalContentMD5.empty()) + { + request.AddHeader( + Details::c_HeaderTransactionalContentMD5, appendDataOptions.TransactionalContentMD5); + } + if (!appendDataOptions.LeaseIdOptional.empty()) + { + request.AddHeader(Details::c_HeaderLeaseIdOptional, appendDataOptions.LeaseIdOptional); + } + if (!appendDataOptions.ClientRequestId.empty()) + { + request.AddHeader(Details::c_HeaderClientRequestId, appendDataOptions.ClientRequestId); + } + request.AddHeader( + Details::c_HeaderApiVersionParameter, appendDataOptions.ApiVersionParameter); + return request; + } + + static PathAppendDataResponse AppendDataParseResponse( + std::unique_ptr responsePtr) + { + /* const */ auto& response = *responsePtr; + if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Accepted) + { + // Append data to file control response. + PathAppendDataResponse result; + if (response.GetHeaders().find(Details::c_HeaderDate) != response.GetHeaders().end()) + { + result.Date = response.GetHeaders().at(Details::c_HeaderDate); + } + if (response.GetHeaders().find(Details::c_HeaderXMsRequestId) + != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at(Details::c_HeaderXMsRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsClientRequestId) + != response.GetHeaders().end()) + { + result.ClientRequestId = response.GetHeaders().at(Details::c_HeaderXMsClientRequestId); + } + if (response.GetHeaders().find(Details::c_HeaderXMsVersion) + != response.GetHeaders().end()) + { + result.Version = response.GetHeaders().at(Details::c_HeaderXMsVersion); + } + return result; + } + else + { + throw Azure::Storage::StorageError::CreateFromResponse(response); + } } }; - static PathUpdateResponse PathUpdateParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) - { - // The data was flushed (written) to the file or the properties were set successfully. Response body is optional and is valid only for "SetAccessControlRecursive" - PathUpdateResponse result = PathUpdateResponse::PathUpdateResponseFromSetAccessControlRecursiveResponse(std::move(SetAccessControlRecursiveResponse::CreateFromJson(nlohmann::json::parse(response.GetBodyBuffer())))); - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) - { - result.ETag = response.GetHeaders().at(k_HeaderETag); - } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderAcceptRanges) != response.GetHeaders().end()) - { - result.AcceptRanges = response.GetHeaders().at(k_HeaderAcceptRanges); - } - if (response.GetHeaders().find(k_HeaderCacheControl) != response.GetHeaders().end()) - { - result.CacheControl = response.GetHeaders().at(k_HeaderCacheControl); - } - if (response.GetHeaders().find(k_HeaderContentDisposition) != response.GetHeaders().end()) - { - result.ContentDisposition = response.GetHeaders().at(k_HeaderContentDisposition); - } - if (response.GetHeaders().find(k_HeaderContentEncoding) != response.GetHeaders().end()) - { - result.ContentEncoding = response.GetHeaders().at(k_HeaderContentEncoding); - } - if (response.GetHeaders().find(k_HeaderContentLanguage) != response.GetHeaders().end()) - { - result.ContentLanguage = response.GetHeaders().at(k_HeaderContentLanguage); - } - if (response.GetHeaders().find(k_HeaderContentLength) != response.GetHeaders().end()) - { - result.ContentLength = std::stoull(response.GetHeaders().at(k_HeaderContentLength)); - } - if (response.GetHeaders().find(k_HeaderContentRange) != response.GetHeaders().end()) - { - result.ContentRange = response.GetHeaders().at(k_HeaderContentRange); - } - if (response.GetHeaders().find(k_HeaderContentType) != response.GetHeaders().end()) - { - result.ContentType = response.GetHeaders().at(k_HeaderContentType); - } - if (response.GetHeaders().find(k_HeaderContentMD5) != response.GetHeaders().end()) - { - result.ContentMD5 = response.GetHeaders().at(k_HeaderContentMD5); - } - if (response.GetHeaders().find(k_HeaderXMsProperties) != response.GetHeaders().end()) - { - result.Properties = response.GetHeaders().at(k_HeaderXMsProperties); - } - if (response.GetHeaders().find(k_HeaderXMsContinuation) != response.GetHeaders().end()) - { - result.XMsContinuation = response.GetHeaders().at(k_HeaderXMsContinuation); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - return result; - } - else if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Accepted) - { - // The uploaded data was accepted. - PathUpdateResponse result; - if (response.GetHeaders().find(k_HeaderContentMD5) != response.GetHeaders().end()) - { - result.ContentMD5 = response.GetHeaders().at(k_HeaderContentMD5); - } - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - return result; - } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static PathUpdateResponse PathUpdate(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const PathUpdateOptions& pathUpdateOptions) - { - // TODO: Pipeline will be added when available. - return PathUpdateParseResponse(transport->Send(context, PathUpdateCreateRequest(std::move(url), pathUpdateOptions))); - } - - struct PathLeaseOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - PathLeaseAction XMsLeaseAction; // There are five lease actions: "acquire", "break", "change", "renew", and "release". Use "acquire" and specify the "x-ms-proposed-lease-id" and "x-ms-lease-duration" to acquire a new lease. Use "break" to break an existing lease. When a lease is broken, the lease break period is allowed to elapse, during which time no lease operation except break and release can be performed on the file. When a lease is successfully broken, the response indicates the interval in seconds until a new lease can be acquired. Use "change" and specify the current lease ID in "x-ms-lease-id" and the new lease ID in "x-ms-proposed-lease-id" to change the lease ID of an active lease. Use "renew" and specify the "x-ms-lease-id" to renew an existing lease. Use "release" and specify the "x-ms-lease-id" to release a lease. - uint32_t XMsLeaseDuration = uint32_t(); // The lease duration is required to acquire a lease, and specifies the duration of the lease in seconds. The lease duration must be between 15 and 60 seconds or -1 for infinite lease. - uint32_t XMsLeaseBreakPeriod = uint32_t(); // The lease break period duration is optional to break a lease, and specifies the break period of the lease in seconds. The lease break duration must be between 0 and 60 seconds. - std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's lease is active and matches this ID. - std::string ProposedLeaseIdOptional; // Proposed lease ID, in a GUID string format. The Blob service returns 400 (Invalid request) if the proposed lease ID is not in the correct format. See Guid Constructor (String) for a list of valid GUID string formats. - std::string IfMatch; // Specify an ETag value to operate only on blobs with a matching value. - std::string IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. - std::string IfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - }; - - static Azure::Core::Http::Request PathLeaseCreateRequest(std::string url, const PathLeaseOptions& pathLeaseOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Post, url); - if (!pathLeaseOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, pathLeaseOptions.ClientRequestId); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(pathLeaseOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, pathLeaseOptions.ApiVersionParameter); - request.AddHeader(k_HeaderPathLeaseAction, PathLeaseActionToString(pathLeaseOptions.XMsLeaseAction)); - - // TODO: We should enforce null check here when Optional is ready. - request.AddHeader(k_HeaderXMsLeaseDuration, std::to_string(pathLeaseOptions.XMsLeaseDuration)); - - // TODO: We should enforce null check here when Optional is ready. - request.AddHeader(k_HeaderXMsLeaseBreakPeriod, std::to_string(pathLeaseOptions.XMsLeaseBreakPeriod)); - if (!pathLeaseOptions.LeaseIdOptional.empty()) - { - request.AddHeader(k_HeaderLeaseIdOptional, pathLeaseOptions.LeaseIdOptional); - } - if (!pathLeaseOptions.ProposedLeaseIdOptional.empty()) - { - request.AddHeader(k_HeaderProposedLeaseIdOptional, pathLeaseOptions.ProposedLeaseIdOptional); - } - if (!pathLeaseOptions.IfMatch.empty()) - { - request.AddHeader(k_HeaderIfMatch, pathLeaseOptions.IfMatch); - } - if (!pathLeaseOptions.IfNoneMatch.empty()) - { - request.AddHeader(k_HeaderIfNoneMatch, pathLeaseOptions.IfNoneMatch); - } - if (!pathLeaseOptions.IfModifiedSince.empty()) - { - request.AddHeader(k_HeaderIfModifiedSince, pathLeaseOptions.IfModifiedSince); - } - if (!pathLeaseOptions.IfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderIfUnmodifiedSince, pathLeaseOptions.IfUnmodifiedSince); - } - return request; - } - - struct PathLeaseResponse - { - std::string Date; - std::string ETag; - std::string LastModified; - std::string RequestId; - std::string Version; - std::string LeaseId; - std::string LeaseTime; - }; - - static PathLeaseResponse PathLeaseParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) - { - // The "renew", "change" or "release" action was successful. - PathLeaseResponse result; - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) - { - result.ETag = response.GetHeaders().at(k_HeaderETag); - } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseId) != response.GetHeaders().end()) - { - result.LeaseId = response.GetHeaders().at(k_HeaderXMsLeaseId); - } - return result; - } - else if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Created) - { - // A new lease has been created. The "acquire" action was successful. - PathLeaseResponse result; - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) - { - result.ETag = response.GetHeaders().at(k_HeaderETag); - } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseId) != response.GetHeaders().end()) - { - result.LeaseId = response.GetHeaders().at(k_HeaderXMsLeaseId); - } - return result; - } - else if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Accepted) - { - // The "break" lease action was successful. - PathLeaseResponse result; - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) - { - result.ETag = response.GetHeaders().at(k_HeaderETag); - } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseTime) != response.GetHeaders().end()) - { - result.LeaseTime = response.GetHeaders().at(k_HeaderXMsLeaseTime); - } - return result; - } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static PathLeaseResponse PathLease(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const PathLeaseOptions& pathLeaseOptions) - { - // TODO: Pipeline will be added when available. - return PathLeaseParseResponse(transport->Send(context, PathLeaseCreateRequest(std::move(url), pathLeaseOptions))); - } - - struct PathReadOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - std::string Range; // The HTTP Range request header specifies one or more byte ranges of the resource to be retrieved. - std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's lease is active and matches this ID. - bool XMsRangeGetContentMd5 = bool(); // Optional. When this header is set to "true" and specified together with the Range header, the service returns the MD5 hash for the range, as long as the range is less than or equal to 4MB in size. If this header is specified without the Range header, the service returns status code 400 (Bad Request). If this header is set to true when the range exceeds 4 MB in size, the service returns status code 400 (Bad Request). - std::string IfMatch; // Specify an ETag value to operate only on blobs with a matching value. - std::string IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. - std::string IfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - }; - - static Azure::Core::Http::Request PathReadCreateRequest(std::string url, const PathReadOptions& pathReadOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Get, url); - if (!pathReadOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, pathReadOptions.ClientRequestId); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(pathReadOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, pathReadOptions.ApiVersionParameter); - if (!pathReadOptions.Range.empty()) - { - request.AddHeader(k_HeaderRange, pathReadOptions.Range); - } - if (!pathReadOptions.LeaseIdOptional.empty()) - { - request.AddHeader(k_HeaderLeaseIdOptional, pathReadOptions.LeaseIdOptional); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddHeader(k_HeaderXMsRangeGetContentMd5, (pathReadOptions.XMsRangeGetContentMd5 ? "true" : "false")); - if (!pathReadOptions.IfMatch.empty()) - { - request.AddHeader(k_HeaderIfMatch, pathReadOptions.IfMatch); - } - if (!pathReadOptions.IfNoneMatch.empty()) - { - request.AddHeader(k_HeaderIfNoneMatch, pathReadOptions.IfNoneMatch); - } - if (!pathReadOptions.IfModifiedSince.empty()) - { - request.AddHeader(k_HeaderIfModifiedSince, pathReadOptions.IfModifiedSince); - } - if (!pathReadOptions.IfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderIfUnmodifiedSince, pathReadOptions.IfUnmodifiedSince); - } - return request; - } - - struct PathReadResponse - { - std::vector BodyBuffer; - std::string AcceptRanges; - std::string CacheControl; - std::string ContentDisposition; - std::string ContentEncoding; - std::string ContentLanguage; - uint64_t ContentLength = uint64_t(); - std::string ContentRange; - std::string ContentType; - std::string ContentMD5; - std::string Date; - std::string ETag; - std::string LastModified; - std::string RequestId; - std::string Version; - std::string ResourceType; - std::string Properties; - std::string LeaseDuration; - std::string LeaseState; - std::string LeaseStatus; - std::string XMsContentMd5; - }; - - static PathReadResponse PathReadParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) - { - // Ok - PathReadResponse result; - result.BodyBuffer = response.GetBodyBuffer(); - if (response.GetHeaders().find(k_HeaderAcceptRanges) != response.GetHeaders().end()) - { - result.AcceptRanges = response.GetHeaders().at(k_HeaderAcceptRanges); - } - if (response.GetHeaders().find(k_HeaderCacheControl) != response.GetHeaders().end()) - { - result.CacheControl = response.GetHeaders().at(k_HeaderCacheControl); - } - if (response.GetHeaders().find(k_HeaderContentDisposition) != response.GetHeaders().end()) - { - result.ContentDisposition = response.GetHeaders().at(k_HeaderContentDisposition); - } - if (response.GetHeaders().find(k_HeaderContentEncoding) != response.GetHeaders().end()) - { - result.ContentEncoding = response.GetHeaders().at(k_HeaderContentEncoding); - } - if (response.GetHeaders().find(k_HeaderContentLanguage) != response.GetHeaders().end()) - { - result.ContentLanguage = response.GetHeaders().at(k_HeaderContentLanguage); - } - if (response.GetHeaders().find(k_HeaderContentLength) != response.GetHeaders().end()) - { - result.ContentLength = std::stoull(response.GetHeaders().at(k_HeaderContentLength)); - } - if (response.GetHeaders().find(k_HeaderContentRange) != response.GetHeaders().end()) - { - result.ContentRange = response.GetHeaders().at(k_HeaderContentRange); - } - if (response.GetHeaders().find(k_HeaderContentType) != response.GetHeaders().end()) - { - result.ContentType = response.GetHeaders().at(k_HeaderContentType); - } - if (response.GetHeaders().find(k_HeaderContentMD5) != response.GetHeaders().end()) - { - result.ContentMD5 = response.GetHeaders().at(k_HeaderContentMD5); - } - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) - { - result.ETag = response.GetHeaders().at(k_HeaderETag); - } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - if (response.GetHeaders().find(k_HeaderXMsResourceType) != response.GetHeaders().end()) - { - result.ResourceType = response.GetHeaders().at(k_HeaderXMsResourceType); - } - if (response.GetHeaders().find(k_HeaderXMsProperties) != response.GetHeaders().end()) - { - result.Properties = response.GetHeaders().at(k_HeaderXMsProperties); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseDuration) != response.GetHeaders().end()) - { - result.LeaseDuration = response.GetHeaders().at(k_HeaderXMsLeaseDuration); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseState) != response.GetHeaders().end()) - { - result.LeaseState = response.GetHeaders().at(k_HeaderXMsLeaseState); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseStatus) != response.GetHeaders().end()) - { - result.LeaseStatus = response.GetHeaders().at(k_HeaderXMsLeaseStatus); - } - return result; - } - else if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::PartialContent) - { - // Partial content - PathReadResponse result; - result.BodyBuffer = response.GetBodyBuffer(); - if (response.GetHeaders().find(k_HeaderAcceptRanges) != response.GetHeaders().end()) - { - result.AcceptRanges = response.GetHeaders().at(k_HeaderAcceptRanges); - } - if (response.GetHeaders().find(k_HeaderCacheControl) != response.GetHeaders().end()) - { - result.CacheControl = response.GetHeaders().at(k_HeaderCacheControl); - } - if (response.GetHeaders().find(k_HeaderContentDisposition) != response.GetHeaders().end()) - { - result.ContentDisposition = response.GetHeaders().at(k_HeaderContentDisposition); - } - if (response.GetHeaders().find(k_HeaderContentEncoding) != response.GetHeaders().end()) - { - result.ContentEncoding = response.GetHeaders().at(k_HeaderContentEncoding); - } - if (response.GetHeaders().find(k_HeaderContentLanguage) != response.GetHeaders().end()) - { - result.ContentLanguage = response.GetHeaders().at(k_HeaderContentLanguage); - } - if (response.GetHeaders().find(k_HeaderContentLength) != response.GetHeaders().end()) - { - result.ContentLength = std::stoull(response.GetHeaders().at(k_HeaderContentLength)); - } - if (response.GetHeaders().find(k_HeaderContentRange) != response.GetHeaders().end()) - { - result.ContentRange = response.GetHeaders().at(k_HeaderContentRange); - } - if (response.GetHeaders().find(k_HeaderContentType) != response.GetHeaders().end()) - { - result.ContentType = response.GetHeaders().at(k_HeaderContentType); - } - if (response.GetHeaders().find(k_HeaderContentMD5) != response.GetHeaders().end()) - { - result.ContentMD5 = response.GetHeaders().at(k_HeaderContentMD5); - } - if (response.GetHeaders().find(k_HeaderXMsContentMd5) != response.GetHeaders().end()) - { - result.XMsContentMd5 = response.GetHeaders().at(k_HeaderXMsContentMd5); - } - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) - { - result.ETag = response.GetHeaders().at(k_HeaderETag); - } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - if (response.GetHeaders().find(k_HeaderXMsResourceType) != response.GetHeaders().end()) - { - result.ResourceType = response.GetHeaders().at(k_HeaderXMsResourceType); - } - if (response.GetHeaders().find(k_HeaderXMsProperties) != response.GetHeaders().end()) - { - result.Properties = response.GetHeaders().at(k_HeaderXMsProperties); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseDuration) != response.GetHeaders().end()) - { - result.LeaseDuration = response.GetHeaders().at(k_HeaderXMsLeaseDuration); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseState) != response.GetHeaders().end()) - { - result.LeaseState = response.GetHeaders().at(k_HeaderXMsLeaseState); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseStatus) != response.GetHeaders().end()) - { - result.LeaseStatus = response.GetHeaders().at(k_HeaderXMsLeaseStatus); - } - return result; - } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static PathReadResponse PathRead(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const PathReadOptions& pathReadOptions) - { - // TODO: Pipeline will be added when available. - return PathReadParseResponse(transport->Send(context, PathReadCreateRequest(std::move(url), pathReadOptions))); - } - - struct PathGetPropertiesOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - PathGetPropertiesAction Action = PathGetPropertiesAction::Unknown; // Optional. If the value is "getStatus" only the system defined properties for the path are returned. If the value is "getAccessControl" the access control list is returned in the response headers (Hierarchical Namespace must be enabled for the account), otherwise the properties are returned. - bool Upn = bool(); // Optional. Valid only when Hierarchical Namespace is enabled for the account. If "true", the user identity values returned in the x-ms-owner, x-ms-group, and x-ms-acl response headers will be transformed from Azure Active Directory Object IDs to User Principal Names. If "false", the values will be returned as Azure Active Directory Object IDs. The default value is false. Note that group and application Object IDs are not translated because they do not have unique friendly names. - std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's lease is active and matches this ID. - std::string IfMatch; // Specify an ETag value to operate only on blobs with a matching value. - std::string IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. - std::string IfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - }; - - static Azure::Core::Http::Request PathGetPropertiesCreateRequest(std::string url, const PathGetPropertiesOptions& pathGetPropertiesOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Head, url); - if (!pathGetPropertiesOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, pathGetPropertiesOptions.ClientRequestId); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(pathGetPropertiesOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, pathGetPropertiesOptions.ApiVersionParameter); - if (pathGetPropertiesOptions.Action != PathGetPropertiesAction::Unknown) - { - request.AddQueryParameter(k_QueryPathGetPropertiesAction, PathGetPropertiesActionToString(pathGetPropertiesOptions.Action)); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryUpn, (pathGetPropertiesOptions.Upn ? "true" : "false")); - if (!pathGetPropertiesOptions.LeaseIdOptional.empty()) - { - request.AddHeader(k_HeaderLeaseIdOptional, pathGetPropertiesOptions.LeaseIdOptional); - } - if (!pathGetPropertiesOptions.IfMatch.empty()) - { - request.AddHeader(k_HeaderIfMatch, pathGetPropertiesOptions.IfMatch); - } - if (!pathGetPropertiesOptions.IfNoneMatch.empty()) - { - request.AddHeader(k_HeaderIfNoneMatch, pathGetPropertiesOptions.IfNoneMatch); - } - if (!pathGetPropertiesOptions.IfModifiedSince.empty()) - { - request.AddHeader(k_HeaderIfModifiedSince, pathGetPropertiesOptions.IfModifiedSince); - } - if (!pathGetPropertiesOptions.IfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderIfUnmodifiedSince, pathGetPropertiesOptions.IfUnmodifiedSince); - } - return request; - } - - struct PathGetPropertiesResponse - { - std::string AcceptRanges; - std::string CacheControl; - std::string ContentDisposition; - std::string ContentEncoding; - std::string ContentLanguage; - uint64_t ContentLength = uint64_t(); - std::string ContentRange; - std::string ContentType; - std::string ContentMD5; - std::string Date; - std::string ETag; - std::string LastModified; - std::string RequestId; - std::string Version; - std::string ResourceType; - std::string Properties; - std::string Owner; - std::string Group; - std::string Permissions; - std::string ACL; - std::string LeaseDuration; - std::string LeaseState; - std::string LeaseStatus; - }; - - static PathGetPropertiesResponse PathGetPropertiesParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) - { - // Returns all properties for the file or directory. - PathGetPropertiesResponse result; - if (response.GetHeaders().find(k_HeaderAcceptRanges) != response.GetHeaders().end()) - { - result.AcceptRanges = response.GetHeaders().at(k_HeaderAcceptRanges); - } - if (response.GetHeaders().find(k_HeaderCacheControl) != response.GetHeaders().end()) - { - result.CacheControl = response.GetHeaders().at(k_HeaderCacheControl); - } - if (response.GetHeaders().find(k_HeaderContentDisposition) != response.GetHeaders().end()) - { - result.ContentDisposition = response.GetHeaders().at(k_HeaderContentDisposition); - } - if (response.GetHeaders().find(k_HeaderContentEncoding) != response.GetHeaders().end()) - { - result.ContentEncoding = response.GetHeaders().at(k_HeaderContentEncoding); - } - if (response.GetHeaders().find(k_HeaderContentLanguage) != response.GetHeaders().end()) - { - result.ContentLanguage = response.GetHeaders().at(k_HeaderContentLanguage); - } - if (response.GetHeaders().find(k_HeaderContentLength) != response.GetHeaders().end()) - { - result.ContentLength = std::stoull(response.GetHeaders().at(k_HeaderContentLength)); - } - if (response.GetHeaders().find(k_HeaderContentRange) != response.GetHeaders().end()) - { - result.ContentRange = response.GetHeaders().at(k_HeaderContentRange); - } - if (response.GetHeaders().find(k_HeaderContentType) != response.GetHeaders().end()) - { - result.ContentType = response.GetHeaders().at(k_HeaderContentType); - } - if (response.GetHeaders().find(k_HeaderContentMD5) != response.GetHeaders().end()) - { - result.ContentMD5 = response.GetHeaders().at(k_HeaderContentMD5); - } - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) - { - result.ETag = response.GetHeaders().at(k_HeaderETag); - } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - if (response.GetHeaders().find(k_HeaderXMsResourceType) != response.GetHeaders().end()) - { - result.ResourceType = response.GetHeaders().at(k_HeaderXMsResourceType); - } - if (response.GetHeaders().find(k_HeaderXMsProperties) != response.GetHeaders().end()) - { - result.Properties = response.GetHeaders().at(k_HeaderXMsProperties); - } - if (response.GetHeaders().find(k_HeaderXMsOwner) != response.GetHeaders().end()) - { - result.Owner = response.GetHeaders().at(k_HeaderXMsOwner); - } - if (response.GetHeaders().find(k_HeaderXMsGroup) != response.GetHeaders().end()) - { - result.Group = response.GetHeaders().at(k_HeaderXMsGroup); - } - if (response.GetHeaders().find(k_HeaderXMsPermissions) != response.GetHeaders().end()) - { - result.Permissions = response.GetHeaders().at(k_HeaderXMsPermissions); - } - if (response.GetHeaders().find(k_HeaderXMsAcl) != response.GetHeaders().end()) - { - result.ACL = response.GetHeaders().at(k_HeaderXMsAcl); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseDuration) != response.GetHeaders().end()) - { - result.LeaseDuration = response.GetHeaders().at(k_HeaderXMsLeaseDuration); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseState) != response.GetHeaders().end()) - { - result.LeaseState = response.GetHeaders().at(k_HeaderXMsLeaseState); - } - if (response.GetHeaders().find(k_HeaderXMsLeaseStatus) != response.GetHeaders().end()) - { - result.LeaseStatus = response.GetHeaders().at(k_HeaderXMsLeaseStatus); - } - return result; - } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static PathGetPropertiesResponse PathGetProperties(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const PathGetPropertiesOptions& pathGetPropertiesOptions) - { - // TODO: Pipeline will be added when available. - return PathGetPropertiesParseResponse(transport->Send(context, PathGetPropertiesCreateRequest(std::move(url), pathGetPropertiesOptions))); - } - - struct PathDeleteOptions - { - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - bool RecursiveOptional = bool(); // Required - std::string Continuation; // Optional. When deleting a directory, the number of paths that are deleted with each invocation is limited. If the number of paths to be deleted exceeds this limit, a continuation token is returned in this response header. When a continuation token is returned in the response, it must be specified in a subsequent invocation of the delete operation to continue deleting the directory. - std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's lease is active and matches this ID. - std::string IfMatch; // Specify an ETag value to operate only on blobs with a matching value. - std::string IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. - std::string IfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - }; - - static Azure::Core::Http::Request PathDeleteCreateRequest(std::string url, const PathDeleteOptions& pathDeleteOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Delete, url); - if (!pathDeleteOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, pathDeleteOptions.ClientRequestId); - } - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(pathDeleteOptions.Timeout)); - request.AddHeader(k_HeaderApiVersionParameter, pathDeleteOptions.ApiVersionParameter); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryRecursiveOptional, (pathDeleteOptions.RecursiveOptional ? "true" : "false")); - if (!pathDeleteOptions.Continuation.empty()) - { - request.AddQueryParameter(k_QueryContinuation, pathDeleteOptions.Continuation); - } - if (!pathDeleteOptions.LeaseIdOptional.empty()) - { - request.AddHeader(k_HeaderLeaseIdOptional, pathDeleteOptions.LeaseIdOptional); - } - if (!pathDeleteOptions.IfMatch.empty()) - { - request.AddHeader(k_HeaderIfMatch, pathDeleteOptions.IfMatch); - } - if (!pathDeleteOptions.IfNoneMatch.empty()) - { - request.AddHeader(k_HeaderIfNoneMatch, pathDeleteOptions.IfNoneMatch); - } - if (!pathDeleteOptions.IfModifiedSince.empty()) - { - request.AddHeader(k_HeaderIfModifiedSince, pathDeleteOptions.IfModifiedSince); - } - if (!pathDeleteOptions.IfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderIfUnmodifiedSince, pathDeleteOptions.IfUnmodifiedSince); - } - return request; - } - - struct PathDeleteResponse - { - std::string Date; - std::string RequestId; - std::string Version; - std::string Continuation; - }; - - static PathDeleteResponse PathDeleteParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) - { - // The file was deleted. - PathDeleteResponse result; - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - if (response.GetHeaders().find(k_HeaderXMsContinuation) != response.GetHeaders().end()) - { - result.Continuation = response.GetHeaders().at(k_HeaderXMsContinuation); - } - return result; - } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static PathDeleteResponse PathDelete(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const PathDeleteOptions& pathDeleteOptions) - { - // TODO: Pipeline will be added when available. - return PathDeleteParseResponse(transport->Send(context, PathDeleteCreateRequest(std::move(url), pathDeleteOptions))); - } - - struct PathSetAccessControlOptions - { - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's lease is active and matches this ID. - std::string Owner; // Optional. The owner of the blob or directory. - std::string Group; // Optional. The owning group of the blob or directory. - std::string Permissions; // Optional and only valid if Hierarchical Namespace is enabled for the account. Sets POSIX access permissions for the file owner, the file owning group, and others. Each class may be granted read, write, or execute permission. The sticky bit is also supported. Both symbolic (rwxrw-rw-) and 4-digit octal notation (e.g. 0766) are supported. - std::string Acl; // Sets POSIX access control rights on files and directories. The value is a comma-separated list of access control entries. Each access control entry (ACE) consists of a scope, a type, a user or group identifier, and permissions in the format "[scope:][type]:[id]:[permissions]". - std::string IfMatch; // Specify an ETag value to operate only on blobs with a matching value. - std::string IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. - std::string IfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - }; - - static Azure::Core::Http::Request PathSetAccessControlCreateRequest(std::string url, const PathSetAccessControlOptions& pathSetAccessControlOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Patch, url); - request.AddQueryParameter(k_QueryAction, "setAccessControl"); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(pathSetAccessControlOptions.Timeout)); - if (!pathSetAccessControlOptions.LeaseIdOptional.empty()) - { - request.AddHeader(k_HeaderLeaseIdOptional, pathSetAccessControlOptions.LeaseIdOptional); - } - if (!pathSetAccessControlOptions.Owner.empty()) - { - request.AddHeader(k_HeaderOwner, pathSetAccessControlOptions.Owner); - } - if (!pathSetAccessControlOptions.Group.empty()) - { - request.AddHeader(k_HeaderGroup, pathSetAccessControlOptions.Group); - } - if (!pathSetAccessControlOptions.Permissions.empty()) - { - request.AddHeader(k_HeaderPermissions, pathSetAccessControlOptions.Permissions); - } - if (!pathSetAccessControlOptions.Acl.empty()) - { - request.AddHeader(k_HeaderAcl, pathSetAccessControlOptions.Acl); - } - if (!pathSetAccessControlOptions.IfMatch.empty()) - { - request.AddHeader(k_HeaderIfMatch, pathSetAccessControlOptions.IfMatch); - } - if (!pathSetAccessControlOptions.IfNoneMatch.empty()) - { - request.AddHeader(k_HeaderIfNoneMatch, pathSetAccessControlOptions.IfNoneMatch); - } - if (!pathSetAccessControlOptions.IfModifiedSince.empty()) - { - request.AddHeader(k_HeaderIfModifiedSince, pathSetAccessControlOptions.IfModifiedSince); - } - if (!pathSetAccessControlOptions.IfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderIfUnmodifiedSince, pathSetAccessControlOptions.IfUnmodifiedSince); - } - if (!pathSetAccessControlOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, pathSetAccessControlOptions.ClientRequestId); - } - request.AddHeader(k_HeaderApiVersionParameter, pathSetAccessControlOptions.ApiVersionParameter); - return request; - } - - struct PathSetAccessControlResponse - { - std::string Date; - std::string ETag; - std::string LastModified; - std::string ClientRequestId; - std::string RequestId; - std::string Version; - }; - - static PathSetAccessControlResponse PathSetAccessControlParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) - { - // Set directory access control response. - PathSetAccessControlResponse result; - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) - { - result.ETag = response.GetHeaders().at(k_HeaderETag); - } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderXMsClientRequestId) != response.GetHeaders().end()) - { - result.ClientRequestId = response.GetHeaders().at(k_HeaderXMsClientRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - return result; - } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static PathSetAccessControlResponse PathSetAccessControl(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const PathSetAccessControlOptions& pathSetAccessControlOptions) - { - // TODO: Pipeline will be added when available. - return PathSetAccessControlParseResponse(transport->Send(context, PathSetAccessControlCreateRequest(std::move(url), pathSetAccessControlOptions))); - } - - struct PathSetAccessControlRecursiveOptions - { - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - std::string Continuation; // Optional. When deleting a directory, the number of paths that are deleted with each invocation is limited. If the number of paths to be deleted exceeds this limit, a continuation token is returned in this response header. When a continuation token is returned in the response, it must be specified in a subsequent invocation of the delete operation to continue deleting the directory. - PathSetAccessControlRecursiveMode Mode; // Mode "set" sets POSIX access control rights on files and directories, "modify" modifies one or more POSIX access control rights that pre-exist on files and directories, "remove" removes one or more POSIX access control rights that were present earlier on files and directories - uint32_t MaxRecords = uint32_t(); // Optional. It specifies the maximum number of files or directories on which the acl change will be applied. If omitted or greater than 2,000, the request will process up to 2,000 items - std::string Acl; // Sets POSIX access control rights on files and directories. The value is a comma-separated list of access control entries. Each access control entry (ACE) consists of a scope, a type, a user or group identifier, and permissions in the format "[scope:][type]:[id]:[permissions]". - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - }; - - static Azure::Core::Http::Request PathSetAccessControlRecursiveCreateRequest(std::string url, const PathSetAccessControlRecursiveOptions& pathSetAccessControlRecursiveOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Patch, url); - request.AddQueryParameter(k_QueryAction, "setAccessControlRecursive"); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(pathSetAccessControlRecursiveOptions.Timeout)); - if (!pathSetAccessControlRecursiveOptions.Continuation.empty()) - { - request.AddQueryParameter(k_QueryContinuation, pathSetAccessControlRecursiveOptions.Continuation); - } - request.AddQueryParameter(k_QueryPathSetAccessControlRecursiveMode, PathSetAccessControlRecursiveModeToString(pathSetAccessControlRecursiveOptions.Mode)); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryMaxRecords, std::to_string(pathSetAccessControlRecursiveOptions.MaxRecords)); - if (!pathSetAccessControlRecursiveOptions.Acl.empty()) - { - request.AddHeader(k_HeaderAcl, pathSetAccessControlRecursiveOptions.Acl); - } - if (!pathSetAccessControlRecursiveOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, pathSetAccessControlRecursiveOptions.ClientRequestId); - } - request.AddHeader(k_HeaderApiVersionParameter, pathSetAccessControlRecursiveOptions.ApiVersionParameter); - return request; - } - - struct PathSetAccessControlRecursiveResponse - { - std::string Date; - std::string ClientRequestId; - std::string Continuation; - std::string RequestId; - std::string Version; - uint32_t DirectoriesSuccessful = uint32_t(); - uint32_t FilesSuccessful = uint32_t(); - uint32_t FailureCount = uint32_t(); - std::vector FailedEntries; - - static PathSetAccessControlRecursiveResponse PathSetAccessControlRecursiveResponseFromSetAccessControlRecursiveResponse(SetAccessControlRecursiveResponse object) - { - PathSetAccessControlRecursiveResponse result; - result.DirectoriesSuccessful = object.DirectoriesSuccessful; - result.FilesSuccessful = object.FilesSuccessful; - result.FailureCount = object.FailureCount; - result.FailedEntries = std::move(object.FailedEntries); - - return result; - } - }; - - static PathSetAccessControlRecursiveResponse PathSetAccessControlRecursiveParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) - { - // Set directory access control recursive response. - PathSetAccessControlRecursiveResponse result = PathSetAccessControlRecursiveResponse::PathSetAccessControlRecursiveResponseFromSetAccessControlRecursiveResponse(std::move(SetAccessControlRecursiveResponse::CreateFromJson(nlohmann::json::parse(response.GetBodyBuffer())))); - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderXMsClientRequestId) != response.GetHeaders().end()) - { - result.ClientRequestId = response.GetHeaders().at(k_HeaderXMsClientRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsContinuation) != response.GetHeaders().end()) - { - result.Continuation = response.GetHeaders().at(k_HeaderXMsContinuation); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - return result; - } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static PathSetAccessControlRecursiveResponse PathSetAccessControlRecursive(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const PathSetAccessControlRecursiveOptions& pathSetAccessControlRecursiveOptions) - { - // TODO: Pipeline will be added when available. - return PathSetAccessControlRecursiveParseResponse(transport->Send(context, PathSetAccessControlRecursiveCreateRequest(std::move(url), pathSetAccessControlRecursiveOptions))); - } - - struct PathFlushDataOptions - { - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - uint64_t Position = uint64_t(); // This parameter allows the caller to upload data in parallel and control the order in which it is appended to the file. It is required when uploading data to be appended to the file and when flushing previously uploaded data to the file. The value must be the position where the data is to be appended. Uploaded data is not immediately flushed, or written, to the file. To flush, the previously uploaded data must be contiguous, the position parameter must be specified and equal to the length of the file after all data has been written, and there must not be a request entity body included with the request. - bool RetainUncommittedData = bool(); // Valid only for flush operations. If "true", uncommitted data is retained after the flush operation completes; otherwise, the uncommitted data is deleted after the flush operation. The default is false. Data at offsets less than the specified position are written to the file when flush succeeds, but this optional parameter allows data after the flush position to be retained for a future flush operation. - bool Close = bool(); // Azure Storage Events allow applications to receive notifications when files change. When Azure Storage Events are enabled, a file changed event is raised. This event has a property indicating whether this is the final change to distinguish the difference between an intermediate flush to a file stream and the final close of a file stream. The close query parameter is valid only when the action is "flush" and change notifications are enabled. If the value of close is "true" and the flush operation completes successfully, the service raises a file change notification with a property indicating that this is the final update (the file stream has been closed). If "false" a change notification is raised indicating the file has changed. The default is false. This query parameter is set to true by the Hadoop ABFS driver to indicate that the file stream has been closed." - uint64_t ContentLength = uint64_t(); // Required for "Append Data" and "Flush Data". Must be 0 for "Flush Data". Must be the length of the request content in bytes for "Append Data". - std::string ContentMD5; // Specify the transactional md5 for the body, to be validated by the service. - std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's lease is active and matches this ID. - std::string CacheControl; // Optional. Sets the blob's cache control. If specified, this property is stored with the blob and returned with a read request. - std::string ContentType; // Optional. Sets the blob's content type. If specified, this property is stored with the blob and returned with a read request. - std::string ContentDisposition; // Optional. Sets the blob's Content-Disposition header. - std::string ContentEncoding; // Optional. Sets the blob's content encoding. If specified, this property is stored with the blob and returned with a read request. - std::string ContentLanguage; // Optional. Set the blob's content language. If specified, this property is stored with the blob and returned with a read request. - std::string IfMatch; // Specify an ETag value to operate only on blobs with a matching value. - std::string IfNoneMatch; // Specify an ETag value to operate only on blobs without a matching value. - std::string IfModifiedSince; // Specify this header value to operate only on a blob if it has been modified since the specified date/time. - std::string IfUnmodifiedSince; // Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - }; - - static Azure::Core::Http::Request PathFlushDataCreateRequest(std::string url, const PathFlushDataOptions& pathFlushDataOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Patch, url); - request.AddQueryParameter(k_QueryAction, "flush"); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(pathFlushDataOptions.Timeout)); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryPosition, std::to_string(pathFlushDataOptions.Position)); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryRetainUncommittedData, (pathFlushDataOptions.RetainUncommittedData ? "true" : "false")); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryClose, (pathFlushDataOptions.Close ? "true" : "false")); - - // TODO: We should enforce null check here when Optional is ready. - request.AddHeader(k_HeaderContentLength, std::to_string(pathFlushDataOptions.ContentLength)); - if (!pathFlushDataOptions.ContentMD5.empty()) - { - request.AddHeader(k_HeaderContentMD5, pathFlushDataOptions.ContentMD5); - } - if (!pathFlushDataOptions.LeaseIdOptional.empty()) - { - request.AddHeader(k_HeaderLeaseIdOptional, pathFlushDataOptions.LeaseIdOptional); - } - if (!pathFlushDataOptions.CacheControl.empty()) - { - request.AddHeader(k_HeaderCacheControl, pathFlushDataOptions.CacheControl); - } - if (!pathFlushDataOptions.ContentType.empty()) - { - request.AddHeader(k_HeaderContentType, pathFlushDataOptions.ContentType); - } - if (!pathFlushDataOptions.ContentDisposition.empty()) - { - request.AddHeader(k_HeaderContentDisposition, pathFlushDataOptions.ContentDisposition); - } - if (!pathFlushDataOptions.ContentEncoding.empty()) - { - request.AddHeader(k_HeaderContentEncoding, pathFlushDataOptions.ContentEncoding); - } - if (!pathFlushDataOptions.ContentLanguage.empty()) - { - request.AddHeader(k_HeaderContentLanguage, pathFlushDataOptions.ContentLanguage); - } - if (!pathFlushDataOptions.IfMatch.empty()) - { - request.AddHeader(k_HeaderIfMatch, pathFlushDataOptions.IfMatch); - } - if (!pathFlushDataOptions.IfNoneMatch.empty()) - { - request.AddHeader(k_HeaderIfNoneMatch, pathFlushDataOptions.IfNoneMatch); - } - if (!pathFlushDataOptions.IfModifiedSince.empty()) - { - request.AddHeader(k_HeaderIfModifiedSince, pathFlushDataOptions.IfModifiedSince); - } - if (!pathFlushDataOptions.IfUnmodifiedSince.empty()) - { - request.AddHeader(k_HeaderIfUnmodifiedSince, pathFlushDataOptions.IfUnmodifiedSince); - } - if (!pathFlushDataOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, pathFlushDataOptions.ClientRequestId); - } - request.AddHeader(k_HeaderApiVersionParameter, pathFlushDataOptions.ApiVersionParameter); - return request; - } - - struct PathFlushDataResponse - { - std::string Date; - std::string ETag; - std::string LastModified; - uint64_t ContentLength = uint64_t(); - std::string ClientRequestId; - std::string RequestId; - std::string Version; - }; - - static PathFlushDataResponse PathFlushDataParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) - { - // The data was flushed (written) to the file successfully. - PathFlushDataResponse result; - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderETag) != response.GetHeaders().end()) - { - result.ETag = response.GetHeaders().at(k_HeaderETag); - } - if (response.GetHeaders().find(k_HeaderLastModified) != response.GetHeaders().end()) - { - result.LastModified = response.GetHeaders().at(k_HeaderLastModified); - } - if (response.GetHeaders().find(k_HeaderContentLength) != response.GetHeaders().end()) - { - result.ContentLength = std::stoull(response.GetHeaders().at(k_HeaderContentLength)); - } - if (response.GetHeaders().find(k_HeaderXMsClientRequestId) != response.GetHeaders().end()) - { - result.ClientRequestId = response.GetHeaders().at(k_HeaderXMsClientRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - return result; - } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static PathFlushDataResponse PathFlushData(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const PathFlushDataOptions& pathFlushDataOptions) - { - // TODO: Pipeline will be added when available. - return PathFlushDataParseResponse(transport->Send(context, PathFlushDataCreateRequest(std::move(url), pathFlushDataOptions))); - } - - struct PathAppendDataOptions - { - uint64_t Position = uint64_t(); // This parameter allows the caller to upload data in parallel and control the order in which it is appended to the file. It is required when uploading data to be appended to the file and when flushing previously uploaded data to the file. The value must be the position where the data is to be appended. Uploaded data is not immediately flushed, or written, to the file. To flush, the previously uploaded data must be contiguous, the position parameter must be specified and equal to the length of the file after all data has been written, and there must not be a request entity body included with the request. - uint32_t Timeout = uint32_t(); // The timeout parameter is expressed in seconds. For more information, see Setting Timeouts for Blob Service Operations. - uint64_t ContentLength = uint64_t(); // Required for "Append Data" and "Flush Data". Must be 0 for "Flush Data". Must be the length of the request content in bytes for "Append Data". - std::string TransactionalContentMD5; // Specify the transactional md5 for the body, to be validated by the service. - std::string LeaseIdOptional; // If specified, the operation only succeeds if the resource's lease is active and matches this ID. - std::vector Body; // Initial data - std::string ClientRequestId; // Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. - std::string ApiVersionParameter = k_DefaultServiceApiVersion; // Specifies the version of the operation to use for this request. - }; - - static Azure::Core::Http::Request PathAppendDataCreateRequest(std::string url, const PathAppendDataOptions& pathAppendDataOptions) - { - Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Patch, std::move(url), pathAppendDataOptions.Body); - request.AddQueryParameter(k_QueryAction, "append"); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryPosition, std::to_string(pathAppendDataOptions.Position)); - - // TODO: We should enforce null check here when Optional is ready. - request.AddQueryParameter(k_QueryTimeout, std::to_string(pathAppendDataOptions.Timeout)); - - // TODO: We should enforce null check here when Optional is ready. - request.AddHeader(k_HeaderContentLength, std::to_string(pathAppendDataOptions.ContentLength)); - if (!pathAppendDataOptions.TransactionalContentMD5.empty()) - { - request.AddHeader(k_HeaderTransactionalContentMD5, pathAppendDataOptions.TransactionalContentMD5); - } - if (!pathAppendDataOptions.LeaseIdOptional.empty()) - { - request.AddHeader(k_HeaderLeaseIdOptional, pathAppendDataOptions.LeaseIdOptional); - } - if (!pathAppendDataOptions.ClientRequestId.empty()) - { - request.AddHeader(k_HeaderClientRequestId, pathAppendDataOptions.ClientRequestId); - } - request.AddHeader(k_HeaderApiVersionParameter, pathAppendDataOptions.ApiVersionParameter); - return request; - } - - struct PathAppendDataResponse - { - std::string Date; - std::string RequestId; - std::string ClientRequestId; - std::string Version; - }; - - static PathAppendDataResponse PathAppendDataParseResponse(/*const*/ std::unique_ptr& responsePtr) - { - /* const */ auto& response = *responsePtr; - if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Accepted) - { - // Append data to file control response. - PathAppendDataResponse result; - if (response.GetHeaders().find(k_HeaderDate) != response.GetHeaders().end()) - { - result.Date = response.GetHeaders().at(k_HeaderDate); - } - if (response.GetHeaders().find(k_HeaderXMsRequestId) != response.GetHeaders().end()) - { - result.RequestId = response.GetHeaders().at(k_HeaderXMsRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsClientRequestId) != response.GetHeaders().end()) - { - result.ClientRequestId = response.GetHeaders().at(k_HeaderXMsClientRequestId); - } - if (response.GetHeaders().find(k_HeaderXMsVersion) != response.GetHeaders().end()) - { - result.Version = response.GetHeaders().at(k_HeaderXMsVersion); - } - return result; - } - else - { - std::cout << "Error occured:\n" << std::string(response.GetBodyBuffer().begin(), response.GetBodyBuffer().end()) << std::endl; - // throw StorageError::CreateFromXml(XmlWrapper::CreateFromBodyBuffer(response.GetBodyBuffer())); - } - } - - static PathAppendDataResponse PathAppendData(std::string url, std::shared_ptr transport, Azure::Core::Context& context, const PathAppendDataOptions& pathAppendDataOptions) - { - // TODO: Pipeline will be added when available. - return PathAppendDataParseResponse(transport->Send(context, PathAppendDataCreateRequest(std::move(url), pathAppendDataOptions))); - } - }; // class DataLakeRestClient }}} // namespace Azure::Storage::DataLake diff --git a/sdk/storage/inc/datalake/service_client.hpp b/sdk/storage/inc/datalake/service_client.hpp new file mode 100644 index 000000000..1c6f3112d --- /dev/null +++ b/sdk/storage/inc/datalake/service_client.hpp @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common/storage_credential.hpp" +#include "common/storage_url_builder.hpp" +#include "datalake_options.hpp" +#include "http/pipeline.hpp" +#include "protocol/datalake_rest_client.hpp" + +#include +#include + +namespace Azure { namespace Storage { namespace DataLake { + + class FileSystemClient; + + class ServiceClient { + public: + /** + * @brief Create from connection string + * @param connectionString Azure Storage connection string. + * @param options Optional parameters used to initialize the client. + * @return ServiceClient + */ + static ServiceClient CreateFromConnectionString( + const std::string& connectionString, + const ServiceClientOptions& options = ServiceClientOptions()); + + /** + * @brief Shared key authentication client. + * @param serviceUri The service URI this client's request targets. + * @param credential The shared key credential used to initialize the client. + * @param options Optional parameters used to initialize the client. + */ + explicit ServiceClient( + const std::string& serviceUri, + std::shared_ptr credential, + const ServiceClientOptions& options = ServiceClientOptions()); + + /** + * @brief Bearer token authentication client. + * @param serviceUri The service URI this client's request targets. + * @param credential The token credential used to initialize the client. + * @param options Optional parameters used to initialize the client. + */ + explicit ServiceClient( + const std::string& serviceUri, + std::shared_ptr credential, + const ServiceClientOptions& options = ServiceClientOptions()); + + /** + * @brief Anonymous/SAS/customized pipeline auth. + * @param serviceUri The service URI this client's request targets. + * @param options Optional parameters used to initialize the client. + */ + explicit ServiceClient( + const std::string& serviceUri, + const ServiceClientOptions& options = ServiceClientOptions()); + + /** + * @brief Create a FileSystemClient from current ServiceClient + * @param fileSystemName The name of the file system. + * @return FileSystemClient + */ + FileSystemClient GetFileSystemClient(const std::string& fileSystemName) const; + + /** + * @brief List the file systems from the service. + * @param options Optional parameters to list the file systems. + * @return ServiceListFileSystemsResponse + */ + ServiceListFileSystemsResponse ListFileSystems( + const ListFileSystemsOptions& options = ListFileSystemsOptions()) const; + + private: + UrlBuilder m_dfsUri; + UrlBuilder m_blobUri; + std::shared_ptr m_pipeline; + }; +}}} // namespace Azure::Storage::DataLake diff --git a/sdk/storage/sample/CMakeLists.txt b/sdk/storage/sample/CMakeLists.txt index ca2f3ed9c..12d8a776e 100644 --- a/sdk/storage/sample/CMakeLists.txt +++ b/sdk/storage/sample/CMakeLists.txt @@ -1,13 +1,14 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # SPDX-License-Identifier: MIT -cmake_minimum_required (VERSION 3.12) +cmake_minimum_required (VERSION 3.15) if (BUILD_CURL_TRANSPORT) add_executable ( azure-storage-sample - blob_getting_started.cpp + # blob_getting_started.cpp + datalake_getting_started.cpp ) target_link_libraries(azure-storage-sample PRIVATE azure-storage) diff --git a/sdk/storage/sample/datalake_getting_started.cpp b/sdk/storage/sample/datalake_getting_started.cpp index 19de03942..fa0e3a4fa 100644 --- a/sdk/storage/sample/datalake_getting_started.cpp +++ b/sdk/storage/sample/datalake_getting_started.cpp @@ -1,3 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // SPDX-License-Identifier: MIT +#include "datalake/datalake.hpp" + +#include +#include + +int main() +{ + using namespace Azure::Storage::DataLake; + auto client = ServiceClient::CreateFromConnectionString(""); + auto response = client.ListFileSystems(); + return 0; +} diff --git a/sdk/storage/src/common/storage_error.cpp b/sdk/storage/src/common/storage_error.cpp new file mode 100644 index 000000000..052e14358 --- /dev/null +++ b/sdk/storage/src/common/storage_error.cpp @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "common/storage_error.hpp" + +#include "common/xml_wrapper.hpp" +#include "http/http.hpp" + +namespace Azure { namespace Storage { + StorageError StorageError::CreateFromResponse(/* const */ Azure::Core::Http::Response& response) + { + auto bodyBuffer + = Azure::Core::Http::Response::ConstructBodyBufferFromStream(response.GetBodyStream()); + + auto xmlReader + = XmlReader(reinterpret_cast(bodyBuffer->data()), bodyBuffer->size()); + + enum class XmlTagName + { + c_Error, + c_Code, + c_Message, + c_Details, + }; + std::vector path; + std::string code; + std::string message; + std::map details; + + while (true) + { + auto node = xmlReader.Read(); + if (node.Type == XmlNodeType::End) + { + break; + } + else if (node.Type == XmlNodeType::EndTag) + { + if (path.size() > 0) + { + path.pop_back(); + } + else + { + break; + } + } + else if (node.Type == XmlNodeType::StartTag) + { + if (std::strcmp(node.Name, "Error") == 0) + { + path.emplace_back(XmlTagName::c_Error); + } + else if (std::strcmp(node.Name, "Code") == 0) + { + path.emplace_back(XmlTagName::c_Code); + } + else if (std::strcmp(node.Name, "Message") == 0) + { + path.emplace_back(XmlTagName::c_Message); + } + else + { + path.emplace_back(XmlTagName::c_Details); + } + } + else if (node.Type == XmlNodeType::Text) + { + if (path.size() == 2 && path[0] == XmlTagName::c_Error && path[1] == XmlTagName::c_Code) + { + code = node.Value; + } + else if ( + path.size() == 2 && path[0] == XmlTagName::c_Error && path[1] == XmlTagName::c_Message) + { + message = node.Value; + } + else if ( + path.size() == 2 && path[0] == XmlTagName::c_Error && path[1] == XmlTagName::c_Details) + { + details[node.Name] = node.Value; + } + } + } + StorageError result = StorageError(message, code); + + if (response.GetHeaders().find("x-ms-request-id") != response.GetHeaders().end()) + { + result.RequestId = response.GetHeaders().at("x-ms-request-id"); + } + result.Details = std::move(details); + + return result; + } +}} // namespace Azure::Storage diff --git a/sdk/storage/src/datalake/datalake_utilities.cpp b/sdk/storage/src/datalake/datalake_utilities.cpp new file mode 100644 index 000000000..114e008b5 --- /dev/null +++ b/sdk/storage/src/datalake/datalake_utilities.cpp @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "datalake/datalake_utilities.hpp" + +#include "common/crypt.hpp" +#include "datalake/protocol/datalake_rest_client.hpp" + +#include + +namespace Azure { namespace Storage { namespace DataLake { + namespace Details { + UrlBuilder GetBlobUriFromDfsUri(const UrlBuilder& dfsUri) + { + UrlBuilder result = dfsUri; + if (std::regex_match( + dfsUri.GetHost(), std::regex(std::string(".*") + Details::c_PathDnsSuffixDefault))) + { + result.SetHost(std::regex_replace(result.GetHost(), std::regex(".dfs."), ".blob.")); + } + + return result; + } + } // namespace Details + std::map DeserializeMetadata( + const std::string& dataLakePropertiesString) + { + std::map result; + + std::string::const_iterator cur = dataLakePropertiesString.begin(); + + auto getSubstrTillDelimiter + = [](char delimiter, const std::string& string, std::string::const_iterator& cur) { + auto begin = cur; + auto end = std::find(cur, string.end(), delimiter); + cur = end; + if (cur != string.end()) + { + ++cur; + } + return std::string(begin, end); + }; + + while (cur != dataLakePropertiesString.end()) + { + std::string key = getSubstrTillDelimiter('=', dataLakePropertiesString, cur); + std::string val = getSubstrTillDelimiter(',', dataLakePropertiesString, cur); + + if (!key.empty() || !val.empty()) + { + result[std::move(key)] = Base64Decode(val); + } + } + + return result; + } + + inline std::string SerializeMetadata( + const std::map& dataLakePropertiesMap) + { + std::string result; + for (const auto& pair : dataLakePropertiesMap) + { + result.append(pair.first + "=" + Base64Encode(pair.second) + ","); + } + if (!result.empty()) + { + result.pop_back(); + } + return result; + } +}}} // namespace Azure::Storage::DataLake diff --git a/sdk/storage/src/datalake/file_system_client.cpp b/sdk/storage/src/datalake/file_system_client.cpp new file mode 100644 index 000000000..f05886d5a --- /dev/null +++ b/sdk/storage/src/datalake/file_system_client.cpp @@ -0,0 +1,235 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "datalake/file_system_client.hpp" + +#include "blobs/internal/protocol/blob_rest_client.hpp" +#include "common/common_headers_request_policy.hpp" +#include "common/constant.hpp" +#include "common/crypt.hpp" +#include "common/shared_key_policy.hpp" +#include "common/storage_common.hpp" +#include "common/token_credential_policy.hpp" +#include "datalake/datalake_utilities.hpp" +#include "datalake/path_client.hpp" +#include "http/curl/curl.hpp" + +namespace Azure { namespace Storage { namespace DataLake { + + FileSystemClient FileSystemClient::CreateFromConnectionString( + const std::string& connectionString, + const std::string& fileSystemName, + const FileSystemClientOptions& options) + { + auto parsedConnectionString = ParseConnectionString(connectionString); + + std::string accountName; + std::string accountKey; + std::string blobEndpoint; + std::string datalakeEndpoint; + std::string EndpointSuffix; + std::string defaultEndpointsProtocol = Details::c_PathDnsSuffixDefault; + + auto ite + = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagAccountName); + if (ite != parsedConnectionString.end()) + { + accountName = ite->second; + } + ite = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagAccountKey); + if (ite != parsedConnectionString.end()) + { + accountKey = ite->second; + } + ite = parsedConnectionString.find( + Azure::Storage::Details::c_ConnectionStringTagDataLakeEndpoint); + if (ite != parsedConnectionString.end()) + { + datalakeEndpoint = ite->second; + } + else + { + // Blob endpoint should also work due to interop. But honor DFS endpoint first. + ite = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagBlobEndpoint); + if (ite != parsedConnectionString.end()) + { + blobEndpoint + = ("." + (Azure::Storage::Details::c_DfsEndpointIdentifier + ("." + ite->second))); + } + } + ite = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagEndpointSuffix); + if (ite != parsedConnectionString.end()) + { + EndpointSuffix = ite->second; + } + ite = parsedConnectionString.find( + Azure::Storage::Details::c_ConnectionStringTagDefaultEndpointsProtocol); + if (ite != parsedConnectionString.end()) + { + defaultEndpointsProtocol = ite->second; + } + + UrlBuilder builder; + builder.SetScheme(defaultEndpointsProtocol); + if (!datalakeEndpoint.empty()) + { + builder = UrlBuilder(datalakeEndpoint); + } + else if (!blobEndpoint.empty()) + { + builder = UrlBuilder(blobEndpoint); + } + else if (!accountName.empty()) + { + builder.SetHost(accountName + ".dfs." + EndpointSuffix); + } + else + { + throw std::runtime_error("invalid connection string"); + } + + builder.AppendPath(fileSystemName, true); + auto credential = std::make_shared(accountName, accountKey); + + return FileSystemClient(builder.to_string(), credential, options); + } + + FileSystemClient::FileSystemClient( + const std::string& fileSystemUri, + std::shared_ptr credential, + const FileSystemClientOptions& options) + : m_dfsUri(fileSystemUri) + { + m_blobUri = Details::GetBlobUriFromDfsUri(m_dfsUri); + + std::vector> policies; + for (const auto& p : options.policies) + { + policies.emplace_back(std::unique_ptr(p->Clone())); + } + policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique(credential)); + policies.emplace_back(std::make_unique( + std::make_shared())); + m_pipeline = std::make_shared(policies); + } + + FileSystemClient::FileSystemClient( + const std::string& fileSystemUri, + std::shared_ptr credential, + const FileSystemClientOptions& options) + : m_dfsUri(fileSystemUri) + { + m_blobUri = Details::GetBlobUriFromDfsUri(m_dfsUri); + + std::vector> policies; + for (const auto& p : options.policies) + { + policies.emplace_back(std::unique_ptr(p->Clone())); + } + policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique(credential)); + policies.emplace_back(std::make_unique( + std::make_shared())); + m_pipeline = std::make_shared(policies); + } + + FileSystemClient::FileSystemClient( + const std::string& fileSystemUri, + const FileSystemClientOptions& options) + : m_dfsUri(fileSystemUri) + { + m_blobUri = Details::GetBlobUriFromDfsUri(m_dfsUri); + + std::vector> policies; + for (const auto& p : options.policies) + { + policies.emplace_back(std::unique_ptr(p->Clone())); + } + policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique( + std::make_shared())); + m_pipeline = std::make_shared(policies); + } + + PathClient FileSystemClient::GetPathClient(const std::string& path) const + { + PathClient client = PathClient(); + auto builder = m_dfsUri; + builder.AppendPath(path, true); + client.m_dfsUri = std::move(builder); + client.m_blobUri = Details::GetBlobUriFromDfsUri(builder); + client.m_pipeline = m_pipeline; + return client; + } + + FileSystemCreateResponse FileSystemClient::Create(const FileSystemCreateOptions& options) const + { + DataLakeRestClient::FileSystem::CreateOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Properties = SerializeMetadata(options.Metadata); + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::FileSystem::Create( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + FileSystemDeleteResponse FileSystemClient::Delete(const FileSystemDeleteOptions& options) const + { + DataLakeRestClient::FileSystem::DeleteOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::FileSystem::Delete( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + FileSystemGetMetadataResponse FileSystemClient::GetMetadata( + const FileSystemGetMetadataOptions& options) const + { + DataLakeRestClient::FileSystem::GetPropertiesOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Timeout = options.Timeout; + auto result = DataLakeRestClient::FileSystem::GetProperties( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + return FileSystemGetMetadataResponse{ + std::move(result.Date), + std::move(result.ETag), + std::move(result.LastModified), + std::move(result.RequestId), + std::move(result.Version), + DeserializeMetadata(result.Properties), + result.NamespaceEnabled == "true" ? true : false}; + } + + FileSystemSetPropertiesResponse FileSystemClient::SetMetadata( + const std::map& metadata, + const FileSystemSetMetadataOptions& options) const + { + DataLakeRestClient::FileSystem::SetPropertiesOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Properties = SerializeMetadata(metadata); + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::FileSystem::SetProperties( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + FileSystemListPathsResponse FileSystemClient::ListPaths( + bool recursive, + const ListPathsOptions& options) const + { + DataLakeRestClient::FileSystem::ListPathsOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Upn = options.Upn; + protocolLayerOptions.Continuation = options.Continuation; + protocolLayerOptions.MaxResults = options.MaxResults; + protocolLayerOptions.Directory = options.Directory; + protocolLayerOptions.RecursiveRequired = recursive; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::FileSystem::ListPaths( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + +}}} // namespace Azure::Storage::DataLake diff --git a/sdk/storage/src/datalake/path_client.cpp b/sdk/storage/src/datalake/path_client.cpp new file mode 100644 index 000000000..46bbfc424 --- /dev/null +++ b/sdk/storage/src/datalake/path_client.cpp @@ -0,0 +1,414 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "datalake/path_client.hpp" + +#include "common/common_headers_request_policy.hpp" +#include "common/constant.hpp" +#include "common/crypt.hpp" +#include "common/shared_key_policy.hpp" +#include "common/storage_common.hpp" +#include "common/token_credential_policy.hpp" +#include "datalake/datalake_utilities.hpp" +#include "http/curl/curl.hpp" + +namespace Azure { namespace Storage { namespace DataLake { + + PathClient PathClient::CreateFromConnectionString( + const std::string& connectionString, + const std::string& fileSystemName, + const std::string& path, + const PathClientOptions& options) + { + auto parsedConnectionString = ParseConnectionString(connectionString); + + std::string accountName; + std::string accountKey; + std::string blobEndpoint; + std::string datalakeEndpoint; + std::string EndpointSuffix; + std::string defaultEndpointsProtocol = Details::c_PathDnsSuffixDefault; + + auto ite + = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagAccountName); + if (ite != parsedConnectionString.end()) + { + accountName = ite->second; + } + ite = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagAccountKey); + if (ite != parsedConnectionString.end()) + { + accountKey = ite->second; + } + ite = parsedConnectionString.find( + Azure::Storage::Details::c_ConnectionStringTagDataLakeEndpoint); + if (ite != parsedConnectionString.end()) + { + datalakeEndpoint = ite->second; + } + else + { + // Blob endpoint should also work due to interop. But honor DFS endpoint first. + ite = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagBlobEndpoint); + if (ite != parsedConnectionString.end()) + { + blobEndpoint + = ("." + (Azure::Storage::Details::c_DfsEndpointIdentifier + ("." + ite->second))); + } + } + ite = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagEndpointSuffix); + if (ite != parsedConnectionString.end()) + { + EndpointSuffix = ite->second; + } + ite = parsedConnectionString.find( + Azure::Storage::Details::c_ConnectionStringTagDefaultEndpointsProtocol); + if (ite != parsedConnectionString.end()) + { + defaultEndpointsProtocol = ite->second; + } + + UrlBuilder builder; + builder.SetScheme(defaultEndpointsProtocol); + if (!datalakeEndpoint.empty()) + { + builder = UrlBuilder(datalakeEndpoint); + } + else if (!blobEndpoint.empty()) + { + builder = UrlBuilder(blobEndpoint); + } + else if (!accountName.empty()) + { + builder.SetHost(accountName + ".dfs." + EndpointSuffix); + } + else + { + throw std::runtime_error("invalid connection string"); + } + + builder.AppendPath(fileSystemName, true); + builder.AppendPath(path, true); + auto credential = std::make_shared(accountName, accountKey); + + return PathClient(builder.to_string(), credential, options); + } + + PathClient::PathClient( + const std::string& pathUri, + std::shared_ptr credential, + const PathClientOptions& options) + : m_dfsUri(pathUri) + { + m_blobUri = Details::GetBlobUriFromDfsUri(m_dfsUri); + + std::vector> policies; + for (const auto& p : options.policies) + { + policies.emplace_back(std::unique_ptr(p->Clone())); + } + policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique(credential)); + policies.emplace_back(std::make_unique( + std::make_shared())); + m_pipeline = std::make_shared(policies); + } + + PathClient::PathClient( + const std::string& pathUri, + std::shared_ptr credential, + const PathClientOptions& options) + : m_dfsUri(pathUri) + { + m_blobUri = Details::GetBlobUriFromDfsUri(m_dfsUri); + + std::vector> policies; + for (const auto& p : options.policies) + { + policies.emplace_back(std::unique_ptr(p->Clone())); + } + policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique(credential)); + policies.emplace_back(std::make_unique( + std::make_shared())); + m_pipeline = std::make_shared(policies); + } + + PathClient::PathClient(const std::string& pathUri, const PathClientOptions& options) + : m_dfsUri(pathUri) + { + m_blobUri = Details::GetBlobUriFromDfsUri(m_dfsUri); + + std::vector> policies; + for (const auto& p : options.policies) + { + policies.emplace_back(std::unique_ptr(p->Clone())); + } + policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique( + std::make_shared())); + m_pipeline = std::make_shared(policies); + } + + PathAppendDataResponse PathClient::AppendData( + Azure::Core::Http::BodyStream* stream, + int64_t offset, + const PathAppendDataOptions& options) const + { + DataLakeRestClient::Path::AppendDataOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Body = stream; + protocolLayerOptions.Position = offset; + protocolLayerOptions.ContentLength = stream->Length(); + protocolLayerOptions.TransactionalContentMD5 = options.ContentMD5; + protocolLayerOptions.LeaseIdOptional = options.LeaseId; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::Path::AppendData( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + PathFlushDataResponse PathClient::FlushData(int64_t offset, const PathFlushDataOptions& options) + const + { + DataLakeRestClient::Path::FlushDataOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Position = offset; + protocolLayerOptions.RetainUncommittedData = options.RetainUncommittedData; + protocolLayerOptions.Close = options.Close; + protocolLayerOptions.ContentLength = 0; + protocolLayerOptions.ContentMD5 = options.ContentMD5; + protocolLayerOptions.LeaseIdOptional = options.LeaseId; + protocolLayerOptions.CacheControl = options.CacheControl; + protocolLayerOptions.ContentType = options.ContentType; + protocolLayerOptions.ContentDisposition = options.ContentDisposition; + protocolLayerOptions.ContentEncoding = options.ContentEncoding; + protocolLayerOptions.ContentLanguage = options.ContentLanguage; + protocolLayerOptions.IfMatch = options.IfMatch; + protocolLayerOptions.IfNoneMatch = options.IfNoneMatch; + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::Path::FlushData( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + PathSetAccessControlResponse PathClient::SetAccessControl( + const SetAccessControlOptions& options) const + { + DataLakeRestClient::Path::SetAccessControlOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.LeaseIdOptional = options.LeaseId; + protocolLayerOptions.Owner = options.Owner; + protocolLayerOptions.Group = options.Group; + protocolLayerOptions.Permissions = options.Permissions; + protocolLayerOptions.Acl = options.Acl; + protocolLayerOptions.IfMatch = options.IfMatch; + protocolLayerOptions.IfNoneMatch = options.IfNoneMatch; + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::Path::SetAccessControl( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + PathSetAccessControlRecursiveResponse PathClient::SetAccessControlRecursive( + PathSetAccessControlRecursiveMode mode, + const SetAccessControlRecursiveOptions& options) const + { + DataLakeRestClient::Path::SetAccessControlRecursiveOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Mode = mode; + protocolLayerOptions.Continuation = options.Continuation; + protocolLayerOptions.MaxRecords = options.MaxRecords; + protocolLayerOptions.Acl = options.Acl; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::Path::SetAccessControlRecursive( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + PathUpdateResponse PathClient::SetProperties(const SetPathPropertiesOptions& options) const + { + DataLakeRestClient::Path::UpdateOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Action = PathUpdateAction::SetProperties; + protocolLayerOptions.CacheControl = options.CacheControl; + protocolLayerOptions.ContentType = options.ContentType; + protocolLayerOptions.ContentDisposition = options.ContentDisposition; + protocolLayerOptions.ContentEncoding = options.ContentEncoding; + protocolLayerOptions.ContentLanguage = options.ContentLanguage; + protocolLayerOptions.IfMatch = options.IfMatch; + protocolLayerOptions.IfNoneMatch = options.IfNoneMatch; + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.Properties = SerializeMetadata(options.Metadata); + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::Path::Update( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + PathCreateResponse PathClient::Create(const PathCreateOptions& options) const + { + DataLakeRestClient::Path::CreateOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Resource = options.Resource; + protocolLayerOptions.LeaseIdOptional = options.LeaseId; + protocolLayerOptions.CacheControl = options.CacheControl; + protocolLayerOptions.ContentType = options.ContentType; + protocolLayerOptions.ContentDisposition = options.ContentDisposition; + protocolLayerOptions.ContentEncoding = options.ContentEncoding; + protocolLayerOptions.ContentLanguage = options.ContentLanguage; + protocolLayerOptions.IfMatch = options.IfMatch; + protocolLayerOptions.IfNoneMatch = options.IfNoneMatch; + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.Properties = SerializeMetadata(options.Metadata); + protocolLayerOptions.Umask = options.Umask; + protocolLayerOptions.Permissions = options.Permissions; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::Path::Create( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + PathRenameResponse PathClient::Rename(const PathRenameOptions& options) const + { + DataLakeRestClient::Path::CreateOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Resource = options.Resource; + protocolLayerOptions.Continuation = options.Continuation; + protocolLayerOptions.Mode = options.Mode; + protocolLayerOptions.SourceLeaseId = options.SourceLeaseId; + protocolLayerOptions.LeaseIdOptional = options.LeaseId; + protocolLayerOptions.CacheControl = options.CacheControl; + protocolLayerOptions.ContentType = options.ContentType; + protocolLayerOptions.ContentDisposition = options.ContentDisposition; + protocolLayerOptions.ContentEncoding = options.ContentEncoding; + protocolLayerOptions.ContentLanguage = options.ContentLanguage; + protocolLayerOptions.IfMatch = options.IfMatch; + protocolLayerOptions.IfNoneMatch = options.IfNoneMatch; + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.SourceIfMatch = options.SourceIfMatch; + protocolLayerOptions.SourceIfNoneMatch = options.SourceIfNoneMatch; + protocolLayerOptions.SourceIfModifiedSince = options.SourceIfModifiedSince; + protocolLayerOptions.SourceIfUnmodifiedSince = options.SourceIfUnmodifiedSince; + protocolLayerOptions.Properties = SerializeMetadata(options.Metadata); + protocolLayerOptions.Umask = options.Umask; + protocolLayerOptions.RenameSource = options.RenameSource; + protocolLayerOptions.Permissions = options.Permissions; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::Path::Create( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + PathDeleteResponse PathClient::Delete(const PathDeleteOptions& options) const + { + DataLakeRestClient::Path::DeleteOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Continuation = options.Continuation; + protocolLayerOptions.LeaseIdOptional = options.LeaseId; + protocolLayerOptions.IfMatch = options.IfMatch; + protocolLayerOptions.IfNoneMatch = options.IfNoneMatch; + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.RecursiveOptional = options.RecursiveOptional; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::Path::Delete( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + GetPathPropertiesResponse PathClient::GetProperties(const PathGetPropertiesOptions& options) const + { + DataLakeRestClient::Path::GetPropertiesOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Action = options.Action; + protocolLayerOptions.Upn = options.UserPrincipalName; + protocolLayerOptions.LeaseIdOptional = options.LeaseId; + protocolLayerOptions.IfMatch = options.IfMatch; + protocolLayerOptions.IfNoneMatch = options.IfNoneMatch; + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.Timeout = options.Timeout; + auto result = DataLakeRestClient::Path::GetProperties( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + return GetPathPropertiesResponse{ + std::move(result.AcceptRanges), + std::move(result.CacheControl), + std::move(result.ContentDisposition), + std::move(result.ContentEncoding), + std::move(result.ContentLanguage), + result.ContentLength, + std::move(result.ContentRange), + std::move(result.ContentType), + std::move(result.ContentMD5), + std::move(result.Date), + std::move(result.ETag), + std::move(result.LastModified), + std::move(result.RequestId), + std::move(result.Version), + std::move(result.ResourceType), + std::move(result.Owner), + std::move(result.Group), + std::move(result.Permissions), + std::move(result.ACL), + std::move(result.LeaseDuration), + std::move(result.LeaseState), + std::move(result.LeaseStatus), + DeserializeMetadata(result.Properties)}; + } + + PathLeaseResponse PathClient::Lease(const PathLeaseOptions& options) const + { + DataLakeRestClient::Path::LeaseOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.XMsLeaseAction = options.LeaseAction; + protocolLayerOptions.ProposedLeaseIdOptional = options.ProposedLeaseId; + protocolLayerOptions.XMsLeaseDuration = options.LeaseDuration; + protocolLayerOptions.XMsLeaseBreakPeriod = options.LeaseBreakPeriod; + protocolLayerOptions.LeaseIdOptional = options.LeaseId; + protocolLayerOptions.IfMatch = options.IfMatch; + protocolLayerOptions.IfNoneMatch = options.IfNoneMatch; + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::Path::Lease( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + + ReadPathResponse PathClient::Read(const PathReadOptions& options) const + { + DataLakeRestClient::Path::ReadOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Range = options.Range; + protocolLayerOptions.XMsRangeGetContentMd5 = options.RangeGetContentMd5; + protocolLayerOptions.LeaseIdOptional = options.LeaseId; + protocolLayerOptions.IfMatch = options.IfMatch; + protocolLayerOptions.IfNoneMatch = options.IfNoneMatch; + protocolLayerOptions.IfModifiedSince = options.IfModifiedSince; + protocolLayerOptions.IfUnmodifiedSince = options.IfUnmodifiedSince; + protocolLayerOptions.Timeout = options.Timeout; + auto result = DataLakeRestClient::Path::Read( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + return ReadPathResponse{ + result.BodyStream, + std::move(result.AcceptRanges), + std::move(result.CacheControl), + std::move(result.ContentDisposition), + std::move(result.ContentEncoding), + std::move(result.ContentLanguage), + std::move(result.ContentLength), + std::move(result.ContentRange), + std::move(result.ContentType), + std::move(result.ContentMD5), + std::move(result.Date), + std::move(result.ETag), + std::move(result.LastModified), + std::move(result.RequestId), + std::move(result.Version), + std::move(result.ResourceType), + std::move(result.LeaseDuration), + std::move(result.LeaseState), + std::move(result.LeaseStatus), + std::move(result.XMsContentMd5), + DeserializeMetadata(result.Properties)}; + } +}}} // namespace Azure::Storage::DataLake diff --git a/sdk/storage/src/datalake/service_client.cpp b/sdk/storage/src/datalake/service_client.cpp new file mode 100644 index 000000000..8e27adc69 --- /dev/null +++ b/sdk/storage/src/datalake/service_client.cpp @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "datalake/service_client.hpp" + +#include "blobs/internal/protocol/blob_rest_client.hpp" +#include "common/common_headers_request_policy.hpp" +#include "common/constant.hpp" +#include "common/shared_key_policy.hpp" +#include "common/storage_common.hpp" +#include "common/token_credential_policy.hpp" +#include "datalake/datalake_utilities.hpp" +#include "datalake/file_system_client.hpp" +#include "http/curl/curl.hpp" + +namespace Azure { namespace Storage { namespace DataLake { + + ServiceClient ServiceClient::CreateFromConnectionString( + const std::string& connectionString, + const ServiceClientOptions& options) + { + auto parsedConnectionString = ParseConnectionString(connectionString); + + std::string accountName; + std::string accountKey; + std::string blobEndpoint; + std::string datalakeEndpoint; + std::string EndpointSuffix; + std::string defaultEndpointsProtocol = Details::c_PathDnsSuffixDefault; + + auto ite + = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagAccountName); + if (ite != parsedConnectionString.end()) + { + accountName = ite->second; + } + ite = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagAccountKey); + if (ite != parsedConnectionString.end()) + { + accountKey = ite->second; + } + ite = parsedConnectionString.find( + Azure::Storage::Details::c_ConnectionStringTagDataLakeEndpoint); + if (ite != parsedConnectionString.end()) + { + datalakeEndpoint = ite->second; + } + else + { + // Blob endpoint should also work due to interop. But honor DFS endpoint first. + ite = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagBlobEndpoint); + if (ite != parsedConnectionString.end()) + { + blobEndpoint + = ("." + (Azure::Storage::Details::c_DfsEndpointIdentifier + ("." + ite->second))); + } + } + ite = parsedConnectionString.find(Azure::Storage::Details::c_ConnectionStringTagEndpointSuffix); + if (ite != parsedConnectionString.end()) + { + EndpointSuffix = ite->second; + } + ite = parsedConnectionString.find( + Azure::Storage::Details::c_ConnectionStringTagDefaultEndpointsProtocol); + if (ite != parsedConnectionString.end()) + { + defaultEndpointsProtocol = ite->second; + } + + UrlBuilder builder; + builder.SetScheme(defaultEndpointsProtocol); + if (!datalakeEndpoint.empty()) + { + builder = UrlBuilder(datalakeEndpoint); + } + else if (!blobEndpoint.empty()) + { + builder = UrlBuilder(blobEndpoint); + } + else if (!accountName.empty()) + { + builder.SetHost(accountName + ".dfs." + EndpointSuffix); + } + else + { + throw std::runtime_error("invalid connection string"); + } + + auto credential = std::make_shared(accountName, accountKey); + + return ServiceClient(builder.to_string(), credential, options); + } + + ServiceClient::ServiceClient( + const std::string& serviceUri, + std::shared_ptr credential, + const ServiceClientOptions& options) + : m_dfsUri(serviceUri) + { + m_blobUri = Details::GetBlobUriFromDfsUri(m_dfsUri); + + std::vector> policies; + for (const auto& p : options.policies) + { + policies.emplace_back(std::unique_ptr(p->Clone())); + } + policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique(credential)); + policies.emplace_back(std::make_unique( + std::make_shared())); + m_pipeline = std::make_shared(policies); + } + + ServiceClient::ServiceClient( + const std::string& serviceUri, + std::shared_ptr credential, + const ServiceClientOptions& options) + : m_dfsUri(serviceUri) + { + m_blobUri = Details::GetBlobUriFromDfsUri(m_dfsUri); + + std::vector> policies; + for (const auto& p : options.policies) + { + policies.emplace_back(std::unique_ptr(p->Clone())); + } + policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique(credential)); + policies.emplace_back(std::make_unique( + std::make_shared())); + m_pipeline = std::make_shared(policies); + } + + ServiceClient::ServiceClient(const std::string& serviceUri, const ServiceClientOptions& options) + : m_dfsUri(serviceUri) + { + m_blobUri = Details::GetBlobUriFromDfsUri(m_dfsUri); + + std::vector> policies; + for (const auto& p : options.policies) + { + policies.emplace_back(std::unique_ptr(p->Clone())); + } + policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique( + std::make_shared())); + m_pipeline = std::make_shared(policies); + } + + FileSystemClient ServiceClient::GetFileSystemClient(const std::string& fileSystemName) const + { + FileSystemClient client = FileSystemClient(); + auto builder = m_dfsUri; + builder.AppendPath(fileSystemName, true); + client.m_dfsUri = std::move(builder); + client.m_blobUri = Details::GetBlobUriFromDfsUri(builder); + client.m_pipeline = m_pipeline; + return client; + } + + ServiceListFileSystemsResponse ServiceClient::ListFileSystems( + const ListFileSystemsOptions& options) const + { + DataLakeRestClient::Service::ListFileSystemsOptions protocolLayerOptions; + // TODO: Add null check here when Nullable is supported + protocolLayerOptions.Prefix = options.Prefix; + protocolLayerOptions.Continuation = options.Continuation; + protocolLayerOptions.MaxResults = options.MaxResults; + protocolLayerOptions.Timeout = options.Timeout; + return DataLakeRestClient::Service::ListFileSystems( + m_dfsUri.to_string(), *m_pipeline, options.Context, protocolLayerOptions); + } + +}}} // namespace Azure::Storage::DataLake diff --git a/sdk/storage/test/CMakeLists.txt b/sdk/storage/test/CMakeLists.txt index 42431c0c5..16cce36fb 100644 --- a/sdk/storage/test/CMakeLists.txt +++ b/sdk/storage/test/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # SPDX-License-Identifier: MIT -cmake_minimum_required (VERSION 3.12) +cmake_minimum_required (VERSION 3.15) if (MSVC) # There will be many const conparisons in test code.