1 // Copyright 2019 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 // This file provides functions for writing string representations of a few
17 // types to character buffers. Generally, the generic ToString function defined
18 // in "pw_string/to_string.h" should be used instead of these functions.
19
20 #include <cstdint>
21 #include <span>
22 #include <string_view>
23 #include <type_traits>
24
25 #include "pw_status/status_with_size.h"
26 #include "pw_string/util.h"
27
28 namespace pw::string {
29
30 // Returns the number of digits in the decimal representation of the provided
31 // non-negative integer. Returns 1 for 0 or 1 + log base 10 for other numbers.
32 uint_fast8_t DecimalDigitCount(uint64_t integer);
33
34 // Returns the number of digits in the hexadecimal representation of the
35 // provided non-negative integer.
HexDigitCount(uint64_t integer)36 constexpr uint_fast8_t HexDigitCount(uint64_t integer) {
37 return (64u - __builtin_clzll(integer | 1u) + 3u) / 4u;
38 }
39
40 // Writes an integer as a null-terminated string in base 10. Returns the number
41 // of characters written, excluding the null terminator, and the status.
42 //
43 // Numbers are never truncated; if the entire number does not fit, only a null
44 // terminator is written and the status is RESOURCE_EXHAUSTED.
45 //
46 // IntToString is templated, but a single 64-bit integer implementation is used
47 // for all integer types. The template is used for two reasons:
48 //
49 // 1. IntToString(int64_t) and IntToString(uint64_t) overloads are ambiguous
50 // when called with types other than int64_t or uint64_t. Using the
51 // template allows IntToString to be called with any integral type.
52 //
53 // 2. Templating IntToString allows the compiler to emit small functions like
54 // IntToString<int> or IntToString<short> that perform casting / sign
55 // extension on the various integer types. This saves code size, since call
56 // sites pass their arguments directly and casting instructions are shared.
57 //
58 template <typename T>
IntToString(T value,std::span<char> buffer)59 StatusWithSize IntToString(T value, std::span<char> buffer) {
60 if constexpr (std::is_signed_v<T>) {
61 return IntToString<int64_t>(value, buffer);
62 } else {
63 return IntToString<uint64_t>(value, buffer);
64 }
65 }
66
67 template <>
68 StatusWithSize IntToString(uint64_t value, std::span<char> buffer);
69
70 template <>
71 StatusWithSize IntToString(int64_t value, std::span<char> buffer);
72
73 // Writes an integer as a hexadecimal string. Semantics match IntToString. The
74 // output is lowercase without a leading 0x. min_width adds leading zeroes such
75 // that the final string is at least the specified number of characters wide.
76 StatusWithSize IntToHexString(uint64_t value,
77 std::span<char> buffer,
78 uint_fast8_t min_width = 0);
79
80 // Rounds a floating point number to an integer and writes it as a
81 // null-terminated string. Returns the number of characters written, excluding
82 // the null terminator, and the status.
83 //
84 // Numbers are never truncated; if the entire number does not fit, only a null
85 // terminator is written and the status is RESOURCE_EXHAUSTED.
86 //
87 // WARNING: This is NOT a fully-functioning float-printing implementation! It
88 // simply outputs the closest integer, "inf", or "NaN". Floating point numbers
89 // too large to represent as a 64-bit int are treated as infinite.
90 //
91 // Examples:
92 //
93 // FloatAsIntToString(1.25, buffer) -> writes "1" to the buffer
94 // FloatAsIntToString(-4.9, buffer) -> writes "-5" to the buffer
95 // FloatAsIntToString(3.5e20, buffer) -> writes "inf" to the buffer
96 // FloatAsIntToString(INFINITY, buffer) -> writes "-inf" to the buffer
97 // FloatAsIntToString(-NAN, buffer) -> writes "-NaN" to the buffer
98 //
99 StatusWithSize FloatAsIntToString(float value, std::span<char> buffer);
100
101 // Writes a bool as "true" or "false". Semantics match CopyEntireString.
102 StatusWithSize BoolToString(bool value, std::span<char> buffer);
103
104 // String used to represent null pointers.
105 inline constexpr std::string_view kNullPointerString("(null)");
106
107 // Writes the pointer's address or kNullPointerString. Semantics match
108 // CopyEntireString.
109 StatusWithSize PointerToString(const void* pointer, std::span<char> buffer);
110
111 // Specialized form of pw::string::Copy which supports nullptr values.
112 //
113 // Copies the string to the buffer, truncating if the full string does not fit.
114 // Always null terminates if buffer.size() > 0.
115 //
116 // If value is a nullptr, then "(null)" is used as a fallback.
117 //
118 // Returns the number of characters written, excluding the null terminator. If
119 // the string is truncated, the status is RESOURCE_EXHAUSTED.
CopyStringOrNull(const std::string_view & value,std::span<char> buffer)120 inline StatusWithSize CopyStringOrNull(const std::string_view& value,
121 std::span<char> buffer) {
122 return Copy(value, buffer);
123 }
CopyStringOrNull(const char * value,std::span<char> buffer)124 inline StatusWithSize CopyStringOrNull(const char* value,
125 std::span<char> buffer) {
126 if (value == nullptr) {
127 return PointerToString(value, buffer);
128 }
129 return Copy(value, buffer);
130 }
131
132 // Copies the string to the buffer, if the entire string fits. Always null
133 // terminates if buffer.size() > 0.
134 //
135 // If value is a nullptr, then "(null)" is used as a fallback.
136 //
137 // Returns the number of characters written, excluding the null terminator. If
138 // the full string does not fit, only a null terminator is written and the
139 // status is RESOURCE_EXHAUSTED.
140 StatusWithSize CopyEntireStringOrNull(const std::string_view& value,
141 std::span<char> buffer);
142
143 // Same as the string_view form of CopyEntireString, except that if value is a
144 // nullptr, then "(null)" is used as a fallback.
CopyEntireStringOrNull(const char * value,std::span<char> buffer)145 inline StatusWithSize CopyEntireStringOrNull(const char* value,
146 std::span<char> buffer) {
147 if (value == nullptr) {
148 return PointerToString(value, buffer);
149 }
150 return CopyEntireStringOrNull(std::string_view(value), buffer);
151 }
152
153 // This function is a fallback that is called if by ToString if no overload
154 // matches. No definition is provided, so attempting to print an unsupported
155 // type causes a linker error.
156 //
157 // Applications may define pw::string::UnknownTypeToString to support generic
158 // printing for unknown types, if desired. Implementations must follow the
159 // ToString semantics.
160 template <typename T>
161 StatusWithSize UnknownTypeToString(const T& value, std::span<char> buffer);
162
163 } // namespace pw::string
164