1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://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, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15
16 #include <cstddef>
17 #include <span>
18 #include <string_view>
19
20 #include "pw_assert/assert.h"
21 #include "pw_result/result.h"
22 #include "pw_status/status.h"
23 #include "pw_status/status_with_size.h"
24 #include "pw_string/internal/length.h"
25
26 namespace pw {
27 namespace string {
28
29 // Safe alternative to the string_view constructor to avoid the risk of an
30 // unbounded implicit or explicit use of strlen.
31 //
32 // This is strongly recommended over using something like C11's strnlen_s as
33 // a string_view does not require null-termination.
ClampedCString(std::span<const char> str)34 constexpr std::string_view ClampedCString(std::span<const char> str) {
35 return std::string_view(str.data(),
36 internal::ClampedLength(str.data(), str.size()));
37 }
38
ClampedCString(const char * str,size_t max_len)39 constexpr std::string_view ClampedCString(const char* str, size_t max_len) {
40 return ClampedCString(std::span<const char>(str, max_len));
41 }
42
43 // Safe alternative to strlen to calculate the null-terminated length of the
44 // string within the specified span, excluding the null terminator. Like C11's
45 // strnlen_s, the scan for the null-terminator is bounded.
46 //
47 // Returns:
48 // null-terminated length of the string excluding the null terminator.
49 // OutOfRange - if the string is not null-terminated.
50 //
51 // Precondition: The string shall be at a valid pointer.
NullTerminatedLength(std::span<const char> str)52 constexpr pw::Result<size_t> NullTerminatedLength(std::span<const char> str) {
53 PW_DASSERT(str.data() != nullptr);
54
55 const size_t length = internal::ClampedLength(str.data(), str.size());
56 if (length == str.size()) {
57 return Status::OutOfRange();
58 }
59
60 return length;
61 }
62
NullTerminatedLength(const char * str,size_t max_len)63 constexpr pw::Result<size_t> NullTerminatedLength(const char* str,
64 size_t max_len) {
65 return NullTerminatedLength(std::span<const char>(str, max_len));
66 }
67
68 // Copies the source string to the dest, truncating if the full string does not
69 // fit. Always null terminates if dest.size() or num > 0.
70 //
71 // Returns the number of characters written, excluding the null terminator. If
72 // the string is truncated, the status is ResourceExhausted.
73 //
74 // Precondition: The destination and source shall not overlap.
75 // Precondition: The source shall be a valid pointer.
Copy(const std::string_view & source,std::span<char> dest)76 PW_CONSTEXPR_CPP20 inline StatusWithSize Copy(const std::string_view& source,
77 std::span<char> dest) {
78 if (dest.empty()) {
79 return StatusWithSize::ResourceExhausted();
80 }
81
82 const size_t copied = source.copy(dest.data(), dest.size() - 1);
83 dest[copied] = '\0';
84
85 return StatusWithSize(
86 copied == source.size() ? OkStatus() : Status::ResourceExhausted(),
87 copied);
88 }
89
Copy(const char * source,std::span<char> dest)90 PW_CONSTEXPR_CPP20 inline StatusWithSize Copy(const char* source,
91 std::span<char> dest) {
92 PW_DASSERT(source != nullptr);
93 return Copy(ClampedCString(source, dest.size()), dest);
94 }
95
Copy(const char * source,char * dest,size_t num)96 PW_CONSTEXPR_CPP20 inline StatusWithSize Copy(const char* source,
97 char* dest,
98 size_t num) {
99 return Copy(source, std::span<char>(dest, num));
100 }
101
102 } // namespace string
103 } // namespace pw
104