Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iox-#1431: Add unsafe_raw_access to iox::string #2093

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/website/release-notes/iceoryx-unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
- Create iceoryx version header for the C-binding [#1014](https://github.com/eclipse-iceoryx/iceoryx/issues/1014)
- Create macros to deprecate header and code constructs [#2057](https://github.com/eclipse-iceoryx/iceoryx/issues/2057)
- Switch to C++17 on all platforms [#2066](https://github.com/eclipse-iceoryx/iceoryx/issues/2066)
- Implement `unsafe_raw_access` in `iox::string` and add `BufferInfo` struct [#1431](https://github.com/eclipse-iceoryx/iceoryx/issues/1431)

**Bugfixes:**

Expand Down
32 changes: 32 additions & 0 deletions iceoryx_hoofs/buffer/include/iox/buffer_info.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2023, Eclipse Foundation and the iceoryx contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_HOOFS_BUFFER_BUFFER_INFO_HPP
#define IOX_HOOFS_BUFFER_BUFFER_INFO_HPP

#include <cstdint>

namespace iox
{
/// @brief struct used to define the used size and total size of a buffer
struct BufferInfo
{
uint64_t used_size{0};
uint64_t total_size{0};
};

} // namespace iox

#endif
76 changes: 76 additions & 0 deletions iceoryx_hoofs/test/moduletests/test_vocabulary_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "iceoryx_hoofs/testing/fatal_failure.hpp"
#include "iox/string.hpp"
#include "test.hpp"
#include <cstring>

namespace
{
Expand Down Expand Up @@ -643,6 +644,81 @@ TYPED_TEST(stringTyped_test, UnsafeAssignOfNullptrFails)
EXPECT_THAT(this->testSubject.unsafe_assign(nullptr), Eq(false));
}

/// @note void unsafe_raw_access(const std::function<void(char*, const uint64_t, const uint64_t)>& func) noexcept
TYPED_TEST(stringTyped_test, UnsafeRawAccessOfCStringOfSize0ResultsInSize0)
{
::testing::Test::RecordProperty("TEST_ID", "43e10399-445d-42af-80b1-25071590de0a");
this->testSubject.unsafe_raw_access([this](char* str, const auto info) -> uint64_t {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, "");
EXPECT_THAT(info.used_size, this->testSubject.size());
using MyString = typename TestFixture::stringType;
EXPECT_THAT(info.total_size, MyString::capacity() + 1); // real buffer size
return 0U;
});
EXPECT_THAT(this->testSubject.size(), Eq(0U));
EXPECT_THAT(this->testSubject.c_str(), StrEq(""));
}

TYPED_TEST(stringTyped_test, UnsafeRawAccessOfCStringOfSize1ResultsInSize1)
{
::testing::Test::RecordProperty("TEST_ID", "a3a3395e-2b69-400c-876a-1fdf70cf2d4a");
this->testSubject.unsafe_raw_access([this](char* str, const auto info) -> uint64_t {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, "M");
EXPECT_THAT(info.used_size, this->testSubject.size());
using MyString = typename TestFixture::stringType;
EXPECT_THAT(info.total_size, MyString::capacity() + 1); // real buffer size
return 1U;
});
EXPECT_THAT(this->testSubject.size(), Eq(1U));
EXPECT_THAT(this->testSubject.c_str(), StrEq("M"));
}

TYPED_TEST(stringTyped_test, UnsafeRawAccessCStringOfSizeCapaResultsInSizeCapa)
{
::testing::Test::RecordProperty("TEST_ID", "49faad68-52fa-4024-993c-49b05e7cb971");
using MyString = typename TestFixture::stringType;
constexpr auto STRINGCAP = MyString::capacity();
std::vector<char> testCharstring(STRINGCAP, 'M');
testCharstring.emplace_back('\0');
this->testSubject.unsafe_raw_access([&](char* str, const auto) -> uint64_t {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, testCharstring.data());
return STRINGCAP;
});
EXPECT_THAT(this->testSubject.unsafe_assign(testCharstring.data()), Eq(true));
EXPECT_THAT(this->testSubject.size(), Eq(STRINGCAP));
}

elBoberido marked this conversation as resolved.
Show resolved Hide resolved
TYPED_TEST(stringTyped_test, UnsafeRawAccessCStringOutOfBoundFail)
{
::testing::Test::RecordProperty("TEST_ID", "b25c35db-1c0d-4f0e-b4bc-b9430a6696f1");
IOX_EXPECT_FATAL_FAILURE<iox::HoofsError>(
[this] {
this->testSubject.unsafe_raw_access([](char* str, const auto info) -> uint64_t {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, "M");
return info.total_size + 1U;
});
},
iox::HoofsError::EXPECTS_ENSURES_FAILED);
}

TYPED_TEST(stringTyped_test, UnsafeRawAccessCStringWrongLenghtFail)
{
::testing::Test::RecordProperty("TEST_ID", "411f5db1-18b8-45c3-9ad6-3c886fb12a26");
IOX_EXPECT_FATAL_FAILURE<iox::HoofsError>(
[this] {
this->testSubject.unsafe_raw_access([](char* str, const auto) -> uint64_t {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, "M");
return 0U;
});
},
iox::HoofsError::EXPECTS_ENSURES_FAILED);
}

/// @note template <uint64_t N>
/// int64_t compare(const string<N>& other) const noexcept
TYPED_TEST(stringTyped_test, CompareEqStringsResultsInZero)
Expand Down
15 changes: 15 additions & 0 deletions iceoryx_hoofs/vocabulary/include/iox/detail/string.inl
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,21 @@ inline bool string<Capacity>::unsafe_assign(const char* const str) noexcept
return true;
}

template <uint64_t Capacity>
inline void
string<Capacity>::unsafe_raw_access(const iox::function_ref<uint64_t(char*, const iox::BufferInfo info)>& func) noexcept
{
iox::BufferInfo info{m_rawstringSize, Capacity + 1};
uint64_t len = func(m_rawstring, info);

IOX_EXPECTS_WITH_MSG(Capacity >= len,
"unsafe_auto_raw_access failed. Data wrote outside the maximun string capacity of "
<< Capacity);
IOX_EXPECTS_WITH_MSG(m_rawstring[len] == '\0', "String does not have the terminator at the returned size");
m_rawstringSize = len;
}


template <uint64_t Capacity>
template <typename T>
inline IsStringOrCharArray<T, int64_t> string<Capacity>::compare(const T& other) const noexcept
Expand Down
17 changes: 17 additions & 0 deletions iceoryx_hoofs/vocabulary/include/iox/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#ifndef IOX_HOOFS_VOCABULARY_STRING_HPP
#define IOX_HOOFS_VOCABULARY_STRING_HPP

#include "iox/buffer_info.hpp"
#include "iox/detail/string_internal.hpp"
#include "iox/detail/string_type_traits.hpp"
#include "iox/function.hpp"
#include "iox/log/logstream.hpp"
#include "iox/optional.hpp"
#include "iox/type_traits.hpp"
Expand Down Expand Up @@ -423,6 +425,21 @@ class string final
template <typename T>
IsStringOrCharArrayOrChar<T, bool> unsafe_append(const T& str) noexcept;

/// @brief direct access to the string's raw pointer. The access resizes the data with the value returned by the
/// passed function. If the data written has not the terminator at the returned size, a FATAL error occurs.
///
/// @param [in] function func is a function composed by the raw data pointer and the BufferInfo with current size
/// and total size, including the space for the zero termination. The return value is the string length.
///
/// @code
/// iox::string<100> s;
/// s.unsafe_raw_access([] (auto* str, const auto info) {
/// strncpy(str, "Hello World", info.total_size);
/// return strlen("Hello World");
/// });
/// @endcode
void unsafe_raw_access(const iox::function_ref<uint64_t(char*, const iox::BufferInfo info)>& func) noexcept;

/// @brief inserts a iox::string or char array in the range [str[0], str[count]) at position pos. The insertion
/// fails if the string capacity would be exceeded or pos is greater than the string size or count is greater than
/// the string to be inserted.
Expand Down