1/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14==============================================================================*/
15
16// #status: RECOMMENDED
17// #category: operations on strings
18// #summary: Merges strings or numbers with no delimiter.
19//
20#ifndef TENSORFLOW_TSL_PLATFORM_STRCAT_H_
21#define TENSORFLOW_TSL_PLATFORM_STRCAT_H_
22
23#include <string>
24
25#include "tensorflow/tsl/platform/macros.h"
26#include "tensorflow/tsl/platform/numbers.h"
27#include "tensorflow/tsl/platform/stringpiece.h"
28#include "tensorflow/tsl/platform/types.h"
29
30// The AlphaNum type was designed to be used as the parameter type for StrCat().
31// Any routine accepting either a string or a number may accept it.
32// The basic idea is that by accepting a "const AlphaNum &" as an argument
33// to your function, your callers will automatically convert bools, integers,
34// and floating point values to strings for you.
35//
36// NOTE: Use of AlphaNum outside of the "strings" package is unsupported except
37// for the specific case of function parameters of type "AlphaNum" or "const
38// AlphaNum &". In particular, instantiating AlphaNum directly as a stack
39// variable is not supported.
40//
41// Conversion from 8-bit values is not accepted because if it were, then an
42// attempt to pass ':' instead of ":" might result in a 58 ending up in your
43// result.
44//
45// Bools convert to "0" or "1".
46//
47// Floating point values are converted to a string which, if passed to strtod(),
48// would produce the exact same original double (except in case of NaN; all NaNs
49// are considered the same value). We try to keep the string short but it's not
50// guaranteed to be as short as possible.
51//
52// You can convert to Hexadecimal output rather than Decimal output using Hex.
53// To do this, pass strings::Hex(my_int) as a parameter to StrCat. You may
54// specify a minimum field width using a separate parameter, so the equivalent
55// of Printf("%04x", my_int) is StrCat(Hex(my_int, strings::kZeroPad4))
56//
57// This class has implicit constructors.
58namespace tsl {
59namespace strings {
60
61enum PadSpec {
62 kNoPad = 1,
63 kZeroPad2,
64 kZeroPad3,
65 kZeroPad4,
66 kZeroPad5,
67 kZeroPad6,
68 kZeroPad7,
69 kZeroPad8,
70 kZeroPad9,
71 kZeroPad10,
72 kZeroPad11,
73 kZeroPad12,
74 kZeroPad13,
75 kZeroPad14,
76 kZeroPad15,
77 kZeroPad16
78};
79
80struct Hex {
81 uint64 value;
82 enum PadSpec spec;
83 template <class Int>
84 explicit Hex(Int v, PadSpec s = kNoPad) : spec(s) {
85 // Prevent sign-extension by casting integers to
86 // their unsigned counterparts.
87 static_assert(
88 sizeof(v) == 1 || sizeof(v) == 2 || sizeof(v) == 4 || sizeof(v) == 8,
89 "Unknown integer type");
90 value = sizeof(v) == 1 ? static_cast<uint8>(v)
91 : sizeof(v) == 2 ? static_cast<uint16>(v)
92 : sizeof(v) == 4 ? static_cast<uint32>(v)
93 : static_cast<uint64>(v);
94 }
95};
96
97class AlphaNum {
98 // NOLINTBEGIN(google-explicit-constructor)
99 public:
100 // No bool ctor -- bools convert to an integral type.
101 // A bool ctor would also convert incoming pointers (bletch).
102 AlphaNum(int i32) // NOLINT(runtime/explicit)
103 : piece_(digits_, FastInt32ToBufferLeft(i32, digits_)) {}
104 AlphaNum(unsigned int u32) // NOLINT(runtime/explicit)
105 : piece_(digits_, FastUInt32ToBufferLeft(u32, digits_)) {}
106 AlphaNum(long x) // NOLINT(runtime/explicit)
107 : piece_(digits_, FastInt64ToBufferLeft(x, digits_)) {}
108 AlphaNum(unsigned long x) // NOLINT(runtime/explicit)
109 : piece_(digits_, FastUInt64ToBufferLeft(x, digits_)) {}
110 AlphaNum(long long int i64) // NOLINT(runtime/explicit)
111 : piece_(digits_, FastInt64ToBufferLeft(i64, digits_)) {}
112 AlphaNum(unsigned long long int u64) // NOLINT(runtime/explicit)
113 : piece_(digits_, FastUInt64ToBufferLeft(u64, digits_)) {}
114
115 AlphaNum(float f) // NOLINT(runtime/explicit)
116 : piece_(digits_, FloatToBuffer(f, digits_)) {}
117 AlphaNum(double f) // NOLINT(runtime/explicit)
118 : piece_(digits_, DoubleToBuffer(f, digits_)) {}
119 AlphaNum(bfloat16 bf) // NOLINT(runtime/explicit)
120 : piece_(digits_, FloatToBuffer(static_cast<float>(bf), digits_)) {}
121
122 AlphaNum(Hex hex); // NOLINT(runtime/explicit)
123
124 AlphaNum(const char *c_str) : piece_(c_str) {} // NOLINT(runtime/explicit)
125 AlphaNum(const StringPiece &pc) : piece_(pc) {} // NOLINT(runtime/explicit)
126 AlphaNum(const std::string &str) // NOLINT(runtime/explicit)
127 : piece_(str) {}
128 AlphaNum(const tstring &str) // NOLINT(runtime/explicit)
129 : piece_(str) {}
130 template <typename A>
131 AlphaNum(const std::basic_string<char, std::char_traits<char>, A> &str)
132 : piece_(str) {} // NOLINT(runtime/explicit)
133
134 StringPiece::size_type size() const { return piece_.size(); }
135 const char *data() const { return piece_.data(); }
136 StringPiece Piece() const { return piece_; }
137
138 private:
139 StringPiece piece_;
140 char digits_[kFastToBufferSize];
141
142 // Use ":" not ':'
143 AlphaNum(char c); // NOLINT(runtime/explicit)
144
145 // NOLINTEND(google-explicit-constructor)
146 TF_DISALLOW_COPY_AND_ASSIGN(AlphaNum);
147};
148
149// ----------------------------------------------------------------------
150// StrCat()
151// This merges the given strings or numbers, with no delimiter. This
152// is designed to be the fastest possible way to construct a string out
153// of a mix of raw C strings, StringPieces, strings, bool values,
154// and numeric values.
155//
156// Don't use this for user-visible strings. The localization process
157// works poorly on strings built up out of fragments.
158//
159// For clarity and performance, don't use StrCat when appending to a
160// string. In particular, avoid using any of these (anti-)patterns:
161// str.append(StrCat(...))
162// str += StrCat(...)
163// str = StrCat(str, ...)
164// where the last is the worse, with the potential to change a loop
165// from a linear time operation with O(1) dynamic allocations into a
166// quadratic time operation with O(n) dynamic allocations. StrAppend
167// is a better choice than any of the above, subject to the restriction
168// of StrAppend(&str, a, b, c, ...) that none of the a, b, c, ... may
169// be a reference into str.
170// ----------------------------------------------------------------------
171
172// For performance reasons, we have specializations for <= 4 args.
173std::string StrCat(const AlphaNum &a) TF_MUST_USE_RESULT;
174std::string StrCat(const AlphaNum &a, const AlphaNum &b) TF_MUST_USE_RESULT;
175std::string StrCat(const AlphaNum &a, const AlphaNum &b,
176 const AlphaNum &c) TF_MUST_USE_RESULT;
177std::string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
178 const AlphaNum &d) TF_MUST_USE_RESULT;
179
180namespace internal {
181
182// Do not call directly - this is not part of the public API.
183std::string CatPieces(std::initializer_list<StringPiece> pieces);
184void AppendPieces(std::string *dest, std::initializer_list<StringPiece> pieces);
185
186} // namespace internal
187
188// Support 5 or more arguments
189template <typename... AV>
190std::string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
191 const AlphaNum &d, const AlphaNum &e,
192 const AV &...args) TF_MUST_USE_RESULT;
193
194template <typename... AV>
195std::string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
196 const AlphaNum &d, const AlphaNum &e, const AV &...args) {
197 return internal::CatPieces({a.Piece(), b.Piece(), c.Piece(), d.Piece(),
198 e.Piece(),
199 static_cast<const AlphaNum &>(args).Piece()...});
200}
201
202// ----------------------------------------------------------------------
203// StrAppend()
204// Same as above, but adds the output to the given string.
205// WARNING: For speed, StrAppend does not try to check each of its input
206// arguments to be sure that they are not a subset of the string being
207// appended to. That is, while this will work:
208//
209// string s = "foo";
210// s += s;
211//
212// This will not (necessarily) work:
213//
214// string s = "foo";
215// StrAppend(&s, s);
216//
217// Note: while StrCat supports appending up to 26 arguments, StrAppend
218// is currently limited to 9. That's rarely an issue except when
219// automatically transforming StrCat to StrAppend, and can easily be
220// worked around as consecutive calls to StrAppend are quite efficient.
221// ----------------------------------------------------------------------
222
223void StrAppend(std::string *dest, const AlphaNum &a);
224void StrAppend(std::string *dest, const AlphaNum &a, const AlphaNum &b);
225void StrAppend(std::string *dest, const AlphaNum &a, const AlphaNum &b,
226 const AlphaNum &c);
227void StrAppend(std::string *dest, const AlphaNum &a, const AlphaNum &b,
228 const AlphaNum &c, const AlphaNum &d);
229
230// Support 5 or more arguments
231template <typename... AV>
232inline void StrAppend(std::string *dest, const AlphaNum &a, const AlphaNum &b,
233 const AlphaNum &c, const AlphaNum &d, const AlphaNum &e,
234 const AV &...args) {
235 internal::AppendPieces(dest,
236 {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
237 static_cast<const AlphaNum &>(args).Piece()...});
238}
239
240} // namespace strings
241} // namespace tsl
242
243#endif // TENSORFLOW_TSL_PLATFORM_STRCAT_H_
244