1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef UTIL_STRING_UTIL_H
17#define UTIL_STRING_UTIL_H
18
19#include <algorithm>
20#include <cctype>
21#include <cstdint>
22#include <iterator>
23
24#include <base/containers/string_view.h>
25#include <base/containers/vector.h>
26#include <render/namespace.h>
27
28#include "util/log.h"
29
30RENDER_BEGIN_NAMESPACE()
31namespace StringUtil {
32template<class T, size_t N>
33constexpr size_t MaxStringLengthFromArray(T (&)[N])
34{
35    return N - 1u;
36}
37
38inline void CopyStringToArray(const BASE_NS::string_view source, char* target, size_t maxLength)
39{
40    if (source.size() > maxLength) {
41        PLUGIN_LOG_W("CopyStringToArray: string (%zu) longer than %zu", source.size(), maxLength);
42    }
43    size_t const length = source.copy(target, maxLength);
44    target[length] = '\0';
45}
46
47inline bool NotSpace(unsigned char ch)
48{
49    return !std::isspace(static_cast<int>(ch));
50}
51
52// trim from start (in place)
53inline void LTrim(BASE_NS::string_view& string)
54{
55    auto const count = size_t(std::find_if(string.begin(), string.end(), NotSpace) - string.begin());
56    string.remove_prefix(count);
57}
58
59// trim from end (in place)
60inline void RTrim(BASE_NS::string_view& string)
61{
62    auto const count =
63        size_t(std::distance(std::find_if(string.rbegin(), string.rend(), NotSpace).base(), string.end()));
64    string.remove_suffix(count);
65}
66
67// trim from both ends (in place)
68inline size_t Trim(BASE_NS::string_view& string)
69{
70    RTrim(string);
71    LTrim(string);
72    return string.length();
73}
74
75inline BASE_NS::vector<BASE_NS::string_view> Split(
76    const BASE_NS::string_view string, const BASE_NS::string_view delims = "|")
77{
78    BASE_NS::vector<BASE_NS::string_view> output;
79    auto left = string;
80
81    while (!left.empty()) {
82        auto const pos = left.find_first_of(delims);
83
84        auto found = left.substr(0, pos);
85        if (Trim(found) > 0) {
86            output.push_back(found);
87        }
88        if (pos != BASE_NS::string_view::npos) {
89            left.remove_prefix(pos + 1);
90        } else {
91            break;
92        }
93    }
94
95    return output;
96}
97
98// find and replace first instance of "find" with "replace" in "source"
99inline bool FindAndReplaceOne(
100    BASE_NS::string& source, const BASE_NS::string_view find, const BASE_NS::string_view replace)
101{
102    const auto p = source.find(find);
103    if (p != BASE_NS::string::npos) {
104        source.replace(source.begin() + static_cast<BASE_NS::string::difference_type>(p),
105            source.begin() + static_cast<BASE_NS::string::difference_type>(p + find.length()), replace);
106    }
107    return (p != BASE_NS::string::npos);
108}
109// find and replace all instances of "find" with "replace" in "source"
110inline void FindAndReplaceAll(
111    BASE_NS::string& source, const BASE_NS::string_view find, const BASE_NS::string_view replace)
112{
113    while (FindAndReplaceOne(source, find, replace))
114        ;
115}
116} // namespace StringUtil
117RENDER_END_NAMESPACE()
118
119#endif // UTIL_STRING_UTIL_H
120