diff --git a/wxf_parser.h b/wxf_parser.h index fc3a544..7bb3393 100644 --- a/wxf_parser.h +++ b/wxf_parser.h @@ -49,7 +49,9 @@ #include #include #include +#include #include +#include namespace WXF_PARSER { @@ -179,6 +181,11 @@ namespace WXF_PARSER { // move from existing buffer Encoder(std::vector&& buf) : buffer(std::move(buf)) {} + const size_t size() const { return buffer.size(); } + void reserve(const size_t new_size) { buffer.reserve(new_size); } + void resize(const size_t new_size) { buffer.resize(new_size); } + void resize(const size_t new_size, const uint8_t val) { buffer.resize(new_size, val); } + // push ustr directly Encoder& push_ustr(const std::vector& str) { buffer.insert(buffer.end(), str.begin(), str.end()); return *this; } Encoder& push_ustr(const std::string_view str) { @@ -193,6 +200,36 @@ namespace WXF_PARSER { buffer.insert(buffer.end(), (uint8_t*)start, (uint8_t*)end); return *this; } + // generate data as ustr in the back + template + Encoder& generate_back_ustr(const size_t len, F&& func) { + static_assert(std::is_trivially_copyable_v, "T should be trivially copyable"); + static_assert(std::is_invocable_r_v, "func should be callable with size_t and return T"); + size_t old_size = buffer.size(); + buffer.resize(old_size + len * sizeof(T)); + uint8_t* data_ptr = buffer.data() + old_size; + for (size_t i = 0; i < len; i++) { + T value = func(i); + std::memcpy(data_ptr + i * sizeof(T), &value, sizeof(T)); + } + return *this; + } + + // generate transformed data as ustr in the back + template + Encoder& transform_back_ustr(const T2* src_ptr, const size_t len, F&& func) { + static_assert(std::is_trivially_copyable_v, "T1 should be trivially copyable"); + static_assert(std::is_invocable_r_v, "func should be callable with T2 and return T1"); + size_t old_size = buffer.size(); + buffer.resize(old_size + len * sizeof(T1)); + uint8_t* data_ptr = buffer.data() + old_size; + for (size_t i = 0; i < len; i++) { + T1 value = func(src_ptr[i]); + std::memcpy(data_ptr + i * sizeof(T1), &value, sizeof(T1)); + } + return *this; + } + Encoder& push_integer(const int64_t val) { auto num_type = minimal_signed_bits(val); switch (num_type) { @@ -265,8 +302,59 @@ namespace WXF_PARSER { return all_len; } - template - Encoder& push_array(const std::vector& dimension_array, const std::span data, WXF_HEAD type, uint8_t num_type) { + template + requires std::is_invocable_v + Encoder& push_array_data(const std::span data, uint8_t num_type, F&& func = std::identity{}) { + // backup current size + size_t old_size = buffer.size(); + using value_t = std::remove_cvref_t>; + + if constexpr (std::is_integral_v) { + const bool consistent_sign = (num_type >> 2) == (std::is_unsigned_v ? (16 >> 2) : 0); + if (size_of_arr_num_type(num_type) == sizeof(value_t) && consistent_sign) { + if constexpr (std::is_same_v, std::identity>) { + return push_ustr(data.data(), data.size()); + } + else { + return transform_back_ustr(data.data(), data.size(), func); + } + } + +#define APPEND_CASTED_ARRAY_DATA(TYPE) do { \ + transform_back_ustr(data.data(), data.size(), \ + [&](const T& value) { return static_cast(func(value)); }); \ + } while (0) + + switch (num_type) { + case 0: APPEND_CASTED_ARRAY_DATA(int8_t); break; + case 1: APPEND_CASTED_ARRAY_DATA(int16_t); break; + case 2: APPEND_CASTED_ARRAY_DATA(int32_t); break; + case 3: APPEND_CASTED_ARRAY_DATA(int64_t); break; + case 16: APPEND_CASTED_ARRAY_DATA(uint8_t); break; + case 17: APPEND_CASTED_ARRAY_DATA(uint16_t); break; + case 18: APPEND_CASTED_ARRAY_DATA(uint32_t); break; + case 19: APPEND_CASTED_ARRAY_DATA(uint64_t); break; + default: std::cerr << "Encoder::push_array_data: unsupported integer array num_type " + << static_cast(num_type) << "." << std::endl; + buffer.resize(old_size); + break; + } + return *this; +#undef APPEND_CASTED_ARRAY_DATA + } + else { + if constexpr (std::is_same_v, std::identity>) { + return push_ustr(data.data(), data.size()); + } + else { + return transform_back_ustr(data.data(), data.size(), func); + } + } + } + + template + requires std::is_invocable_v + Encoder& push_array(const std::vector& dimension_array, const std::span data, WXF_HEAD type, uint8_t num_type, F&& func = std::identity{}) { // backup current size size_t old_size = buffer.size(); @@ -281,7 +369,74 @@ namespace WXF_PARSER { } // push data - return push_ustr(data.data(), data.size()); + if constexpr (std::is_integral_v) { + const bool supported_num_type = num_type <= 3 || (num_type >= 16 && num_type <= 19); + if (!supported_num_type) { + std::cerr << "Encoder::push_array: unsupported integer array num_type " + << static_cast(num_type) << "." << std::endl; + buffer.resize(old_size); + return *this; + } + } + return push_array_data(data, num_type, std::forward(func)); + } + + template + requires std::is_invocable_v + Encoder& push_generated_array_data(const size_t len, uint8_t num_type, F&& func) { + // backup current size + size_t old_size = buffer.size(); + using value_t = std::remove_cvref_t>; + + if constexpr (std::is_integral_v) { +#define GENERATE_ARRAY_DATA(TYPE) do { \ + generate_back_ustr(len, [&](size_t i) { \ + return static_cast(func(i)); \ + });} while (0) + + switch (num_type) { + case 0: GENERATE_ARRAY_DATA(int8_t); break; + case 1: GENERATE_ARRAY_DATA(int16_t); break; + case 2: GENERATE_ARRAY_DATA(int32_t); break; + case 3: GENERATE_ARRAY_DATA(int64_t); break; + case 16: GENERATE_ARRAY_DATA(uint8_t); break; + case 17: GENERATE_ARRAY_DATA(uint16_t); break; + case 18: GENERATE_ARRAY_DATA(uint32_t); break; + case 19: GENERATE_ARRAY_DATA(uint64_t); break; + default: std::cerr << "Encoder::push_generated_array_data: unsupported integer array num_type " + << static_cast(num_type) << "." << std::endl; + buffer.resize(old_size); + break; + } + return *this; +#undef GENERATE_ARRAY_DATA + } + else { + return generate_back_ustr(len, func); + } + } + + template + requires std::is_invocable_v + Encoder& push_generated_array(const std::vector& dimension_array, WXF_HEAD type, uint8_t num_type, F&& func) { + // backup current size + size_t old_size = buffer.size(); + using value_t = std::remove_cvref_t>; + + // [array_type, num_type, rank, dimensions..., data...] + auto all_len = push_array_info(dimension_array, type, num_type); + + // push data + if constexpr (std::is_integral_v) { + const bool supported_num_type = num_type <= 3 || (num_type >= 16 && num_type <= 19); + if (!supported_num_type) { + std::cerr << "Encoder::push_generated_array: unsupported integer array num_type " + << static_cast(num_type) << "." << std::endl; + buffer.resize(old_size); + return *this; + } + } + return push_generated_array_data(all_len, num_type, std::forward(func)); } template @@ -1314,4 +1469,4 @@ namespace WXF_PARSER { fullform_to_wxf(encoder, FullForm::parse_FullForm(ff_template), map); return encoder; } -} // namespace WXF_PARSER \ No newline at end of file +} // namespace WXF_PARSER