From 2b0a62093eef173b0af3c925b842d5d2a8d6e842 Mon Sep 17 00:00:00 2001 From: "Eunki, Hong" Date: Thu, 2 Feb 2023 08:21:24 +0900 Subject: [PATCH] Reduce decoding time of embedded gltf + Fix base64 encode bug Let we don't copy the full string during decode base64. + Let we print '=' tags end of encoded base64 result, so we can decode the encode result at common external library. Change-Id: I2c671512ffcd986e30a4e773dacdd6e62dca2f31 Signed-off-by: Eunki, Hong --- .../src/dali-toolkit/utc-Dali-Builder.cpp | 116 ++++++++++++++++++++- .../public-api/loader/buffer-definition.cpp | 6 +- .../public-api/loader/material-definition.cpp | 14 ++- dali-toolkit/devel-api/builder/base64-encoding.cpp | 75 +++++++++++-- dali-toolkit/devel-api/builder/base64-encoding.h | 24 ++++- 5 files changed, 212 insertions(+), 23 deletions(-) diff --git a/automated-tests/src/dali-toolkit/utc-Dali-Builder.cpp b/automated-tests/src/dali-toolkit/utc-Dali-Builder.cpp index 802f7b4..f332890 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-Builder.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-Builder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1774,7 +1774,69 @@ int UtcDaliBase64EncodingP(void) std::string output; DALI_TEST_CHECK(value.Get(output)); - DALI_TEST_EQUALS(output, "AAAAAAEAAAACAAAAAwAAAAQAAAAFAAAAAAAAAP////8", TEST_LOCATION); + DALI_TEST_EQUALS(output, "AAAAAAEAAAACAAAAAwAAAAQAAAAFAAAAAAAAAP////8=", TEST_LOCATION); + + std::cout << "Output data: " << output << std::endl; + + END_TEST; +} + +int UtcDaliBase64EncodingP2(void) +{ + std::vector data = {0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()}; + + Property::Value value; + EncodeBase64PropertyData(value, data); + + std::cout << "Input data: "; + std::ostream_iterator out_it(std::cout, ", "); + std::copy(data.begin(), data.end(), out_it); + std::cout << std::endl; + + std::string output; + DALI_TEST_CHECK(value.Get(output)); + DALI_TEST_EQUALS(output, "AAAAAAEAAAACAAAAAwAAAAQAAAAFAAAAAAAAAP////8=", TEST_LOCATION); + + std::cout << "Output data: " << output << std::endl; + + END_TEST; +} + +int UtcDaliBase64EncodingP3(void) +{ + std::string originalData = "Something Longer than 64 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/== length is 106"; + originalData.push_back(-4); + originalData.push_back(-7); // some kind of non-ascii. + originalData.push_back(0); + originalData.push_back(0); + originalData.push_back(2); + originalData.push_back(2); + originalData.push_back(2); + + std::vector data(originalData.begin(), originalData.end()); + + Dali::Property::Value value; + EncodeBase64PropertyData(value, data); + + std::cout << "Input data: "; + std::ostream_iterator out_it(std::cout, ", "); + std::copy(data.begin(), data.end(), out_it); + std::cout << std::endl; + + std::string output; + Dali::Property::Array array; + DALI_TEST_CHECK(value.GetArray()); + array = *value.GetArray(); + DALI_TEST_EQUALS(array.Count(), 3, TEST_LOCATION); + DALI_TEST_CHECK(array[0].Get(output)); + std::cout << "first string : " << output << std::endl; + DALI_TEST_EQUALS(output, "U29tZXRoaW5nIExvbmdlciB0aGFuIDY0IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZX", TEST_LOCATION); + DALI_TEST_CHECK(array[1].Get(output)); + std::cout << "second string : " << output << std::endl; + DALI_TEST_EQUALS(output, "WFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5Ky89PSAgbGVu", TEST_LOCATION); + DALI_TEST_CHECK(array[2].Get(output)); + std::cout << "third string : " << output << std::endl; + DALI_TEST_EQUALS(output, "Z3RoIGlzIDEwNvz5AAACAgI=", TEST_LOCATION); std::cout << "Output data: " << output << std::endl; @@ -1793,14 +1855,22 @@ int UtcDaliBase64EncodingN(void) DALI_TEST_CHECK(value.Get(output)); DALI_TEST_EQUALS(output.empty(), true, TEST_LOCATION); + std::vector data2; + EncodeBase64PropertyData(value, data2); + + DALI_TEST_CHECK(value.Get(output)); + DALI_TEST_EQUALS(output.empty(), true, TEST_LOCATION); + END_TEST; } template int b64l(std::vector& data) { - auto lengthInBytes = 4 * data.size(); - return ceil(lengthInBytes * 1.33333f); + auto lengthInBytes = sizeof(T) * data.size(); + // base64 encode each 3-byte as 4-byte. + // return ceil(lengthInBytes / 3) * 4 + return (lengthInBytes + 2) / 3 * 4; } int UtcDaliBase64EncodingP02(void) @@ -1969,6 +2039,10 @@ int UtcDaliBase64DecodingN01(void) std::vector outputData; DecodeBase64PropertyData(value, outputData); DALI_TEST_EQUALS(outputData.size(), 0, TEST_LOCATION); + + std::vector outputData2; + DecodeBase64PropertyData(value, outputData2); + DALI_TEST_EQUALS(outputData2.size(), 0, TEST_LOCATION); END_TEST; } @@ -1985,6 +2059,10 @@ int UtcDaliBase64DecodingN02(void) std::vector outputData; DecodeBase64PropertyData(value, outputData); DALI_TEST_EQUALS(outputData.size(), 0, TEST_LOCATION); + + std::vector outputData2; + DecodeBase64PropertyData(value, outputData2); + DALI_TEST_EQUALS(outputData2.size(), 0, TEST_LOCATION); END_TEST; } @@ -2002,3 +2080,33 @@ int UtcDaliBase64DecodingP01(void) END_TEST; } + +int UtcDaliBase64DecodingP02(void) +{ + tet_infoline("Test decoding string of known data gives expected result"); + + std::string testInput("//////7+/v4DAgEA"); + std::vector expectedResults = {0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0x03, 0x02, 0x01, 0x00}; + + std::vector outputData; + DecodeBase64PropertyData(Property::Value(testInput), outputData); + + DALI_TEST_EQUALS(std::equal(expectedResults.begin(), expectedResults.end(), outputData.begin()), true, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliBase64DecodingFromString(void) +{ + tet_infoline("Test decoding string of known data gives expected result"); + + std::string testInput("//////7+/v4DAgEA"); + std::vector expectedResults = {0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0x03, 0x02, 0x01, 0x00}; + + std::vector outputData; + DecodeBase64FromString(testInput, outputData); + + DALI_TEST_EQUALS(std::equal(expectedResults.begin(), expectedResults.end(), outputData.begin()), true, TEST_LOCATION); + + END_TEST; +} diff --git a/dali-scene3d/public-api/loader/buffer-definition.cpp b/dali-scene3d/public-api/loader/buffer-definition.cpp index ee89bdc..1704927 100644 --- a/dali-scene3d/public-api/loader/buffer-definition.cpp +++ b/dali-scene3d/public-api/loader/buffer-definition.cpp @@ -38,7 +38,7 @@ static constexpr std::string_view EMBEDDED_DATA_BASE64_ENCODING_TYPE = "base64 struct BufferDefinition::Impl { - std::vector buffer; + std::vector buffer; std::shared_ptr stream; }; @@ -85,9 +85,9 @@ void BufferDefinition::LoadBuffer() if(position != std::string::npos) { position += EMBEDDED_DATA_BASE64_ENCODING_TYPE.length(); - std::string data = mUri.substr(position); + std::string_view data = std::string_view(mUri).substr(position); mImpl.get()->buffer.clear(); - Dali::Toolkit::DecodeBase64PropertyData(data, mImpl.get()->buffer); + Dali::Toolkit::DecodeBase64FromString(data, mImpl.get()->buffer); mImpl.get()->stream = std::make_shared(reinterpret_cast(mImpl.get()->buffer.data()), mByteLength, FileStream::READ | FileStream::BINARY); mIsEmbedded = true; } diff --git a/dali-scene3d/public-api/loader/material-definition.cpp b/dali-scene3d/public-api/loader/material-definition.cpp index bf764ff..58ab65d 100644 --- a/dali-scene3d/public-api/loader/material-definition.cpp +++ b/dali-scene3d/public-api/loader/material-definition.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,9 +91,9 @@ Dali::PixelData LoadImageResource(const std::string& resourcePath, if(position != std::string::npos) { position += EMBEDDED_DATA_BASE64_ENCODING_TYPE.length(); - std::string data = textureDefinition.mImageUri.substr(position); + std::string_view data = std::string_view(textureDefinition.mImageUri).substr(position); std::vector buffer; - Dali::Toolkit::DecodeBase64PropertyData(data, buffer); + Dali::Toolkit::DecodeBase64FromString(data, buffer); uint32_t bufferSize = buffer.size(); Dali::Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromBuffer(reinterpret_cast(buffer.data()), bufferSize, textureDefinition.mMinImageDimensions, fittingMode, textureDefinition.mSamplingMode, orientationCorrection); @@ -187,8 +187,7 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) // Load textures auto iTexture = mTextureStages.begin(); - auto checkStage = [&](uint32_t flags) - { + auto checkStage = [&](uint32_t flags) { return iTexture != mTextureStages.end() && MaskMatch(iTexture->mSemantic, flags); }; @@ -323,7 +322,7 @@ TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environ uint32_t n = 0; for(auto& tData : raw.mTextures) { - auto& pixels = tData.mPixels; + auto& pixels = tData.mPixels; Texture texture; if(pixels) { @@ -379,8 +378,7 @@ TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environ bool MaterialDefinition::CheckTextures(uint32_t flags) const { - return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) - { return MaskMatch(ts.mSemantic, flags); }) != mTextureStages.end(); + return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) { return MaskMatch(ts.mSemantic, flags); }) != mTextureStages.end(); } } // namespace Loader diff --git a/dali-toolkit/devel-api/builder/base64-encoding.cpp b/dali-toolkit/devel-api/builder/base64-encoding.cpp index 2de438a..17b823a 100644 --- a/dali-toolkit/devel-api/builder/base64-encoding.cpp +++ b/dali-toolkit/devel-api/builder/base64-encoding.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,12 +75,13 @@ bool DecodeBase64PropertyData(const Property::Value& value, std::vector outputTmpData; - outputTmpData.reserve(ceil(encodedString.size() * 0.75f)); + std::vector outputTmpData; + // Output required at least ceil(length * 3 / 4) + outputData.reserve((encodedString.size() * 3 + 3) / 4); bn::decode_b64(encodedString.begin(), encodedString.end(), std::back_inserter(outputTmpData)); outputData.clear(); - uint32_t outputSize = outputTmpData.size() / sizeof(uint32_t) + static_cast(!!(outputTmpData.size() % sizeof(uint32_t))); + uint32_t outputSize = (outputTmpData.size() + sizeof(uint32_t) - 1) / sizeof(uint32_t); outputData.resize(outputSize); // Treat as a block of data memcpy(&outputData[0], &outputTmpData[0], outputTmpData.size()); @@ -97,7 +98,8 @@ bool DecodeBase64PropertyData(const Property::Value& value, std::vector if(GetStringFromProperty(value, encodedString)) { - outputData.reserve(ceil(encodedString.size() * 0.75f)); + // Output required at least ceil(length * 3 / 4) + outputData.reserve((encodedString.size() * 3 + 3) / 4); bn::decode_b64(encodedString.begin(), encodedString.end(), std::back_inserter(outputData)); decoded = true; @@ -105,6 +107,15 @@ bool DecodeBase64PropertyData(const Property::Value& value, std::vector return decoded; } +bool DecodeBase64FromString(const std::string_view& encodedString, std::vector& outputData) +{ + // Output required at least ceil(length * 3 / 4) + outputData.reserve((encodedString.size() * 3 + 3) >> 2); + bn::decode_b64(encodedString.begin(), encodedString.end(), std::back_inserter(outputData)); + + return true; // Always success. +} + void EncodeBase64PropertyData(Property::Value& value, const std::vector& inputData) { std::ostringstream oss; @@ -114,11 +125,61 @@ void EncodeBase64PropertyData(Property::Value& value, const std::vector(oss, "")); std::string encodedString = oss.str(); + + // Add padding + int paddingLength = (4 - (encodedString.length() % 4)) % 4; + if(paddingLength > 0) + { + while(paddingLength--) + { + oss << '='; + } + encodedString = oss.str(); + } + + if(encodedString.length() > MAX_PROPERTY_STRING_LENGTH) + { + // cut string up into blocks of MAX_PROPERTY_STRING_LENGTH and store to an array + auto numStrings = (encodedString.length() + MAX_PROPERTY_STRING_LENGTH - 1) / MAX_PROPERTY_STRING_LENGTH; + + Property::Array array; + for(auto i = 0u; i < numStrings; ++i) + { + array.PushBack(encodedString.substr(i * MAX_PROPERTY_STRING_LENGTH, MAX_PROPERTY_STRING_LENGTH)); + } + value = array; + } + else + { + value = encodedString; + } +} + +void EncodeBase64PropertyData(Property::Value& value, const std::vector& inputData) +{ + std::ostringstream oss; + + bn::encode_b64(reinterpret_cast(&inputData[0]), + reinterpret_cast(&inputData[0] + inputData.size()), + std::ostream_iterator(oss, "")); + + std::string encodedString = oss.str(); + + // Add padding + int paddingLength = (4 - (encodedString.length() % 4)) % 4; + if(paddingLength > 0) + { + while(paddingLength--) + { + oss << '='; + } + encodedString = oss.str(); + } + if(encodedString.length() > MAX_PROPERTY_STRING_LENGTH) { // cut string up into blocks of MAX_PROPERTY_STRING_LENGTH and store to an array - auto numStrings = encodedString.length() / MAX_PROPERTY_STRING_LENGTH + - ((encodedString.length() % MAX_PROPERTY_STRING_LENGTH) != 0); + auto numStrings = (encodedString.length() + MAX_PROPERTY_STRING_LENGTH - 1) / MAX_PROPERTY_STRING_LENGTH; Property::Array array; for(auto i = 0u; i < numStrings; ++i) diff --git a/dali-toolkit/devel-api/builder/base64-encoding.h b/dali-toolkit/devel-api/builder/base64-encoding.h index c7067ed..566bded 100644 --- a/dali-toolkit/devel-api/builder/base64-encoding.h +++ b/dali-toolkit/devel-api/builder/base64-encoding.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_BASE64_ENCODING_H /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ // EXTERNAL INCLUDES #include #include +#include // INTERNAL INCLUDES #include @@ -56,6 +57,18 @@ DALI_TOOLKIT_API bool DecodeBase64PropertyData(const Property::Value& value, std DALI_TOOLKIT_API bool DecodeBase64PropertyData(const Property::Value& value, std::vector& outputData); /** + * @brief Parses a std::string_view to retrieve an array of uint8_t data. + * + * Data can be encoded using the base64 encoding scheme to allow it to be used + * in JSON (The property system maps to JSON types). + * + * @param[in] encodedString The input string to decode + * @param[out] outputData The output data block + * @return True if a data block was decoded successfully. + */ +DALI_TOOLKIT_API bool DecodeBase64FromString(const std::string_view& encodedString, std::vector& outputData); + +/** * @brief Convert a block of uint32_t data into a Property::STRING or ARRAY of STRINGs * encoded using base64. This allows the data to be mapped to JSON easily. * @@ -64,6 +77,15 @@ DALI_TOOLKIT_API bool DecodeBase64PropertyData(const Property::Value& value, std */ DALI_TOOLKIT_API void EncodeBase64PropertyData(Property::Value& value, const std::vector& inputData); +/** + * @brief Convert a block of uint8_t data into a Property::STRING or ARRAY of STRINGs + * encoded using base64. This allows the data to be mapped to JSON easily. + * + * @param[out] value The value to write data into (to avoid copying). + * @param[in] inputData The input + */ +DALI_TOOLKIT_API void EncodeBase64PropertyData(Property::Value& value, const std::vector& inputData); + } // namespace Toolkit } // namespace Dali -- 2.7.4