diff options
author | 2021-12-02 09:49:12 -0800 | |
---|---|---|
committer | 2022-01-08 18:55:32 -0800 | |
commit | c74e9e21d569956eb4e132f0521f785c29af3223 (patch) | |
tree | 9e4fd8be1696c505c91f10c6d819db7347107d99 | |
parent | 83bd771ca25844f6f00c7b60e554f6566a7a3222 (diff) |
FTL: Add variadic concatenation
For now, the arguments can only be integers, string literals, and
truncated string views, as this will mainly be used for tracing.
Bug: 185536303
Test: ftl_test
Change-Id: I3141992c6c9438710f04b056e67a5e8811d39f78
-rw-r--r-- | include/ftl/concat.h | 84 | ||||
-rw-r--r-- | include/ftl/details/concat.h | 62 | ||||
-rw-r--r-- | libs/ftl/Android.bp | 1 | ||||
-rw-r--r-- | libs/ftl/concat_test.cpp | 55 |
4 files changed, 202 insertions, 0 deletions
diff --git a/include/ftl/concat.h b/include/ftl/concat.h new file mode 100644 index 0000000000..ded48f7c8c --- /dev/null +++ b/include/ftl/concat.h @@ -0,0 +1,84 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * 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. + */ + +#pragma once + +#include <ftl/details/concat.h> + +namespace android::ftl { + +// Lightweight (not allocating nor sprintf-based) concatenation. +// +// std::string_view name = "Volume"; +// ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB"); +// +// assert(string.str() == "Vol: -3 dB"); +// assert(string.c_str()[string.size()] == '\0'); +// +template <std::size_t, typename... Ts> +struct Concat; + +template <std::size_t N, typename T, typename... Ts> +struct Concat<N, T, Ts...> : Concat<N + details::StaticString<T>::N, Ts...> { + explicit constexpr Concat(T v, Ts... args) { append(v, args...); } + + protected: + constexpr Concat() = default; + + constexpr void append(T v, Ts... args) { + using Str = details::StaticString<T>; + const Str str(v); + + // TODO: Replace with constexpr std::copy in C++20. + for (auto it = str.view.begin(); it != str.view.end();) { + *this->end_++ = *it++; + } + + using Base = Concat<N + Str::N, Ts...>; + this->Base::append(args...); + } +}; + +template <std::size_t N> +struct Concat<N> { + static constexpr std::size_t max_size() { return N; } + constexpr std::size_t size() const { return end_ - buffer_; } + + constexpr const char* c_str() const { return buffer_; } + + constexpr std::string_view str() const { + // TODO: Replace with {buffer_, end_} in C++20. + return {buffer_, size()}; + } + + protected: + constexpr Concat() : end_(buffer_) {} + constexpr void append() { *end_ = '\0'; } + + char buffer_[N + 1]; + char* end_; +}; + +// Deduction guide. +template <typename... Ts> +Concat(Ts&&...) -> Concat<0, Ts...>; + +template <std::size_t N> +constexpr auto truncated(std::string_view v) { + return details::Truncated<N>{v}; +} + +} // namespace android::ftl diff --git a/include/ftl/details/concat.h b/include/ftl/details/concat.h new file mode 100644 index 0000000000..8ce949ef05 --- /dev/null +++ b/include/ftl/details/concat.h @@ -0,0 +1,62 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * 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. + */ + +#pragma once + +#include <functional> +#include <string_view> + +#include <ftl/string.h> + +namespace android::ftl::details { + +template <typename T, typename = void> +struct StaticString; + +template <typename T> +struct StaticString<T, std::enable_if_t<std::is_integral_v<T>>> { + static constexpr std::size_t N = to_chars_length_v<T>; + + explicit StaticString(T v) : view(to_chars(buffer, v)) {} + + to_chars_buffer_t<T> buffer; + const std::string_view view; +}; + +template <std::size_t M> +struct StaticString<const char (&)[M], void> { + static constexpr std::size_t N = M - 1; + + explicit constexpr StaticString(const char (&str)[M]) : view(str, N) {} + + const std::string_view view; +}; + +template <std::size_t N> +struct Truncated { + std::string_view view; +}; + +template <std::size_t M> +struct StaticString<Truncated<M>, void> { + static constexpr std::size_t N = M; + + explicit constexpr StaticString(Truncated<M> str) : view(str.view.substr(0, N)) {} + + const std::string_view view; +}; + +} // namespace android::ftl::details diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 5a80ad067c..bc2eb23677 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -16,6 +16,7 @@ cc_test { srcs: [ "Flags_test.cpp", "cast_test.cpp", + "concat_test.cpp", "enum_test.cpp", "future_test.cpp", "small_map_test.cpp", diff --git a/libs/ftl/concat_test.cpp b/libs/ftl/concat_test.cpp new file mode 100644 index 0000000000..8ecb1b252d --- /dev/null +++ b/libs/ftl/concat_test.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * 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. + */ + +#include <ftl/concat.h> +#include <gtest/gtest.h> + +namespace android::test { + +// Keep in sync with example usage in header file. +TEST(Concat, Example) { + std::string_view name = "Volume"; + ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB"); + + EXPECT_EQ(string.str(), "Vol: -3 dB"); + EXPECT_EQ(string.c_str()[string.size()], '\0'); +} + +namespace { + +static_assert(ftl::Concat{"foo"}.str() == "foo"); +static_assert(ftl::Concat{ftl::truncated<3>("foobar")}.str() == "foo"); + +constexpr ftl::Concat kConcat{"po", "trz", "ebie"}; + +static_assert(kConcat.size() == 9); +static_assert(kConcat.max_size() == 9); +static_assert(kConcat.str() == "potrzebie"); +static_assert(kConcat.str() == std::string_view(kConcat.c_str())); + +constexpr auto concat() { + return ftl::Concat{ftl::truncated<1>("v???"), ftl::truncated<2>("ee??"), + ftl::truncated<3>("ble?"), ftl::truncated<4>("fetz"), + ftl::truncated<90>("er")}; +} + +static_assert(concat().size() == 12); +static_assert(concat().max_size() == 100); +static_assert(concat().str() == "veeblefetzer"); +static_assert(concat().str() == std::string_view(concat().c_str())); + +} // namespace +} // namespace android::test |