1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2
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 TENSORFLOW_CORE_PLATFORM_CTSTRING_INTERNAL_H_
17 #define TENSORFLOW_CORE_PLATFORM_CTSTRING_INTERNAL_H_
18
19 #include <limits.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
25 __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
26 defined(_WIN32)
27 #define TF_TSTRING_LITTLE_ENDIAN 1
28 #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
29 __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
30 #define TF_TSTRING_LITTLE_ENDIAN 0
31 #else
32 #error "Unable to detect endianness."
33 #endif
34
35 #if defined(__clang__) || \
36 (defined(__GNUC__) && \
37 ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5))
TF_swap32(uint32_t host_int)38 static inline uint32_t TF_swap32(uint32_t host_int) {
39 return __builtin_bswap32(host_int);
40 }
41
42 #elif defined(_MSC_VER)
TF_swap32(uint32_t host_int)43 static inline uint32_t TF_swap32(uint32_t host_int) {
44 return _byteswap_ulong(host_int);
45 }
46
47 #elif defined(__APPLE__)
TF_swap32(uint32_t host_int)48 static inline uint32_t TF_swap32(uint32_t host_int) {
49 return OSSwapInt32(host_int);
50 }
51
52 #else
TF_swap32(uint32_t host_int)53 static inline uint32_t TF_swap32(uint32_t host_int) {
54 #if defined(__GLIBC__)
55 return bswap_32(host_int);
56 #else // defined(__GLIBC__)
57 return (((host_int & uint32_t{0xFF}) << 24) |
58 ((host_int & uint32_t{0xFF00}) << 8) |
59 ((host_int & uint32_t{0xFF0000}) >> 8) |
60 ((host_int & uint32_t{0xFF000000}) >> 24));
61 #endif // defined(__GLIBC__)
62 }
63 #endif
64
65 #if TF_TSTRING_LITTLE_ENDIAN
66 #define TF_le32toh(x) TF_swap32(x)
67 #else // TF_TSTRING_LITTLE_ENDIAN
68 #define TF_le32toh(x) x
69 #endif // TF_TSTRING_LITTLE_ENDIAN
70
TF_align16(size_t i)71 static inline size_t TF_align16(size_t i) { return (i + 0xF) & ~0xF; }
72
TF_max(size_t a,size_t b)73 static inline size_t TF_max(size_t a, size_t b) { return a > b ? a : b; }
TF_min(size_t a,size_t b)74 static inline size_t TF_min(size_t a, size_t b) { return a < b ? a : b; }
75
76 typedef enum TF_TString_Type { // NOLINT
77 TF_TSTR_SMALL = 0x00,
78 TF_TSTR_LARGE = 0x01,
79 TF_TSTR_OFFSET = 0x02,
80 TF_TSTR_VIEW = 0x03,
81 TF_TSTR_TYPE_MASK = 0x03
82 } TF_TString_Type;
83
84 typedef struct TF_TString_Large { // NOLINT
85 size_t size;
86 size_t cap;
87 char *ptr;
88 } TF_TString_Large;
89
90 typedef struct TF_TString_Offset { // NOLINT
91 uint32_t size;
92 uint32_t offset;
93 uint32_t count;
94 } TF_TString_Offset;
95
96 typedef struct TF_TString_View { // NOLINT
97 size_t size;
98 const char *ptr;
99 } TF_TString_View;
100
101 typedef struct TF_TString_Raw { // NOLINT
102 uint8_t raw[24];
103 } TF_TString_Raw;
104
105 typedef union TF_TString_Union { // NOLINT
106 TF_TString_Large large;
107 TF_TString_Offset offset;
108 TF_TString_View view;
109 TF_TString_Raw raw;
110 } TF_TString_Union;
111
112 enum {
113 TF_TString_SmallCapacity =
114 (sizeof(TF_TString_Union) - sizeof(/* null delim */ char) -
115 sizeof(/* uint8_t size */ uint8_t)),
116 };
117
118 typedef struct TF_TString_Small { // NOLINT
119 uint8_t size;
120 char str[TF_TString_SmallCapacity + sizeof(/* null delim */ char)];
121 } TF_TString_Small;
122
123 typedef struct TF_TString { // NOLINT
124 union {
125 // small conflicts with '#define small char' in RpcNdr.h for MSVC, so we use
126 // smll instead.
127 TF_TString_Small smll;
128 TF_TString_Large large;
129 TF_TString_Offset offset;
130 TF_TString_View view;
131 TF_TString_Raw raw;
132 } u;
133 } TF_TString;
134
135 // TODO(dero): Fix for OSS, and add C only build test.
136 // _Static_assert(CHAR_BIT == 8);
137 // _Static_assert(sizeof(TF_TString) == 24);
138
TF_TString_GetType(const TF_TString * str)139 extern inline TF_TString_Type TF_TString_GetType(const TF_TString *str) {
140 return (TF_TString_Type)(str->u.raw.raw[0] & TF_TSTR_TYPE_MASK); // NOLINT
141 }
142
143 // XXX(dero): For the big-endian case, this function could potentially be more
144 // performant and readable by always storing the string size as little-endian
145 // and always byte-swapping on big endian, resulting in a simple 'bswap'+'shr'
146 // (for architectures that have a bswap op).
TF_TString_ToActualSizeT(size_t size)147 static inline size_t TF_TString_ToActualSizeT(size_t size) {
148 #ifdef TF_TSTRING_LITTLE_ENDIAN
149 return size >> 2;
150 #else // TF_TSTRING_LITTLE_ENDIAN
151 // 0xFF000000 or 0xFF00000000000000 depending on platform
152 static const size_t mask = ~((~(size_t)0) >> 8);
153
154 return (((mask << 2) & size) >> 2) | (~mask & size);
155 #endif // TF_TSTRING_LITTLE_ENDIAN
156 }
157
TF_TString_ToInternalSizeT(size_t size,TF_TString_Type type)158 static inline size_t TF_TString_ToInternalSizeT(size_t size,
159 TF_TString_Type type) {
160 #ifdef TF_TSTRING_LITTLE_ENDIAN
161 return (size << 2) | type;
162 #else // TF_TSTRING_LITTLE_ENDIAN
163 // 0xFF000000 or 0xFF00000000000000 depending on platform
164 static const size_t mask = ~((~(size_t)0) >> 8);
165
166 return (mask & (size << 2)) | (~mask & size) |
167 ((size_t)type << ((sizeof(size_t) - 1) * 8)); // NOLINT
168 #endif // TF_TSTRING_LITTLE_ENDIAN
169 }
170
TF_TString_Init(TF_TString * str)171 extern inline void TF_TString_Init(TF_TString *str) {
172 str->u.smll.size = 0;
173 str->u.smll.str[0] = '\0';
174 }
175
TF_TString_Dealloc(TF_TString * str)176 extern inline void TF_TString_Dealloc(TF_TString *str) {
177 if (TF_TString_GetType(str) == TF_TSTR_LARGE &&
178 str->u.large.ptr != NULL) { // NOLINT
179 free(str->u.large.ptr);
180 TF_TString_Init(str);
181 }
182 }
183
TF_TString_GetSize(const TF_TString * str)184 extern inline size_t TF_TString_GetSize(const TF_TString *str) {
185 switch (TF_TString_GetType(str)) {
186 case TF_TSTR_SMALL:
187 return str->u.smll.size >> 2;
188 case TF_TSTR_LARGE:
189 return TF_TString_ToActualSizeT(str->u.large.size);
190 case TF_TSTR_OFFSET:
191 return TF_le32toh(str->u.offset.size) >> 2;
192 case TF_TSTR_VIEW:
193 return TF_TString_ToActualSizeT(str->u.view.size);
194 default:
195 return 0; // Unreachable.
196 }
197 }
198
TF_TString_GetCapacity(const TF_TString * str)199 extern inline size_t TF_TString_GetCapacity(const TF_TString *str) {
200 switch (TF_TString_GetType(str)) {
201 case TF_TSTR_SMALL:
202 return TF_TString_SmallCapacity;
203 case TF_TSTR_LARGE:
204 return str->u.large.cap;
205 case TF_TSTR_OFFSET:
206 case TF_TSTR_VIEW:
207 default:
208 return 0;
209 }
210 }
211
TF_TString_GetDataPointer(const TF_TString * str)212 extern inline const char *TF_TString_GetDataPointer(const TF_TString *str) {
213 switch (TF_TString_GetType(str)) {
214 case TF_TSTR_SMALL:
215 return str->u.smll.str;
216 case TF_TSTR_LARGE:
217 return str->u.large.ptr;
218 case TF_TSTR_OFFSET:
219 return (const char *)str + str->u.offset.offset; // NOLINT
220 case TF_TSTR_VIEW:
221 return str->u.view.ptr;
222 default:
223 // Unreachable.
224 return NULL; // NOLINT
225 }
226 }
227
TF_TString_ResizeUninitialized(TF_TString * str,size_t new_size)228 extern inline char *TF_TString_ResizeUninitialized(TF_TString *str,
229 size_t new_size) {
230 size_t curr_size = TF_TString_GetSize(str);
231 size_t copy_size = TF_min(new_size, curr_size);
232
233 TF_TString_Type curr_type = TF_TString_GetType(str);
234 const char *curr_ptr = TF_TString_GetDataPointer(str);
235
236 // Case: SMALL/LARGE/VIEW/OFFSET -> SMALL
237 if (new_size <= TF_TString_SmallCapacity) {
238 str->u.smll.size = (uint8_t)((new_size << 2) | TF_TSTR_SMALL); // NOLINT
239 str->u.smll.str[new_size] = '\0';
240
241 if (curr_type != TF_TSTR_SMALL && copy_size) {
242 memcpy(str->u.smll.str, curr_ptr, copy_size);
243 }
244
245 if (curr_type == TF_TSTR_LARGE) {
246 free((void *)curr_ptr); // NOLINT
247 }
248
249 // We do not clear out the newly excluded region.
250
251 return str->u.smll.str;
252 }
253
254 // Case: SMALL/LARGE/VIEW/OFFSET -> LARGE
255 size_t new_cap;
256 size_t curr_cap = TF_TString_GetCapacity(str);
257 // We assume SIZE_MAX % 16 == 0.
258 size_t curr_cap_x2 = curr_cap >= SIZE_MAX / 2 ? SIZE_MAX - 1 : curr_cap * 2;
259
260 if (new_size < curr_size && new_size < curr_cap / 2) {
261 // TODO(dero): Replace with shrink_to_fit flag.
262 new_cap = TF_align16(curr_cap / 2 + 1) - 1;
263 } else if (new_size > curr_cap_x2) {
264 new_cap = TF_align16(new_size + 1) - 1;
265 } else if (new_size > curr_cap) {
266 new_cap = TF_align16(curr_cap_x2 + 1) - 1;
267 } else {
268 new_cap = curr_cap;
269 }
270
271 char *new_ptr;
272 if (new_cap == curr_cap) {
273 new_ptr = str->u.large.ptr;
274 } else if (curr_type == TF_TSTR_LARGE) {
275 new_ptr = (char *)realloc(str->u.large.ptr, new_cap + 1); // NOLINT
276 } else {
277 new_ptr = (char *)malloc(new_cap + 1); // NOLINT
278 if (copy_size) {
279 memcpy(new_ptr, curr_ptr, copy_size);
280 }
281 }
282
283 str->u.large.size = TF_TString_ToInternalSizeT(new_size, TF_TSTR_LARGE);
284 str->u.large.ptr = new_ptr;
285 str->u.large.ptr[new_size] = '\0';
286 str->u.large.cap = new_cap;
287
288 return str->u.large.ptr;
289 }
290
TF_TString_GetMutableDataPointer(TF_TString * str)291 extern inline char *TF_TString_GetMutableDataPointer(TF_TString *str) {
292 switch (TF_TString_GetType(str)) {
293 case TF_TSTR_SMALL:
294 return str->u.smll.str;
295 case TF_TSTR_OFFSET:
296 case TF_TSTR_VIEW:
297 // Convert OFFSET/VIEW to LARGE
298 TF_TString_ResizeUninitialized(str, TF_TString_GetSize(str));
299 return str->u.large.ptr;
300 case TF_TSTR_LARGE:
301 return str->u.large.ptr;
302 default:
303 // Unreachable.
304 return NULL; // NOLINT
305 }
306 }
307
TF_TString_Reserve(TF_TString * str,size_t new_cap)308 extern inline void TF_TString_Reserve(TF_TString *str, size_t new_cap) {
309 TF_TString_Type curr_type = TF_TString_GetType(str);
310
311 if (new_cap <= TF_TString_SmallCapacity) {
312 // We do nothing, we let Resize/GetMutableDataPointer handle the
313 // conversion to SMALL from VIEW/OFFSET when the need arises.
314 // In the degenerate case, where new_cap <= TF_TString_SmallCapacity,
315 // curr_size > TF_TString_SmallCapacity, and the type is VIEW/OFFSET, we
316 // defer the malloc to Resize/GetMutableDataPointer.
317 return;
318 }
319
320 if (curr_type == TF_TSTR_LARGE && new_cap <= str->u.large.cap) {
321 // We handle reduced cap in resize.
322 return;
323 }
324
325 // Case: VIEW/OFFSET -> LARGE or grow an existing LARGE type
326 size_t curr_size = TF_TString_GetSize(str);
327 const char *curr_ptr = TF_TString_GetDataPointer(str);
328
329 // Since VIEW and OFFSET types are read-only, their capacity is effectively 0.
330 // So we make sure we have enough room in the VIEW and OFFSET cases.
331 new_cap = TF_align16(TF_max(new_cap, curr_size) + 1) - 1;
332
333 if (curr_type == TF_TSTR_LARGE) {
334 str->u.large.ptr =
335 (char *)realloc(str->u.large.ptr, new_cap + 1); // NOLINT
336 } else {
337 // Convert to Large
338 char *new_ptr = (char *)malloc(new_cap + 1); // NOLINT
339 memcpy(new_ptr, curr_ptr, curr_size);
340
341 str->u.large.size = TF_TString_ToInternalSizeT(curr_size, TF_TSTR_LARGE);
342 str->u.large.ptr = new_ptr;
343 str->u.large.ptr[curr_size] = '\0';
344 }
345
346 str->u.large.cap = new_cap;
347 }
348
TF_TString_Resize(TF_TString * str,size_t new_size,char c)349 extern inline char *TF_TString_Resize(TF_TString *str, size_t new_size,
350 char c) {
351 size_t curr_size = TF_TString_GetSize(str);
352 char *cstr = TF_TString_ResizeUninitialized(str, new_size);
353
354 if (new_size > curr_size) {
355 memset(cstr + curr_size, c, new_size - curr_size);
356 }
357
358 return cstr;
359 }
360
TF_TString_AssignView(TF_TString * dst,const char * src,size_t size)361 extern inline void TF_TString_AssignView(TF_TString *dst, const char *src,
362 size_t size) {
363 TF_TString_Dealloc(dst);
364
365 dst->u.view.size = TF_TString_ToInternalSizeT(size, TF_TSTR_VIEW);
366 dst->u.view.ptr = src;
367 }
368
TF_TString_AppendN(TF_TString * dst,const char * src,size_t src_size)369 extern inline void TF_TString_AppendN(TF_TString *dst, const char *src,
370 size_t src_size) {
371 if (!src_size) return;
372
373 size_t dst_size = TF_TString_GetSize(dst);
374
375 char *dst_c = TF_TString_ResizeUninitialized(dst, dst_size + src_size);
376
377 memcpy(dst_c + dst_size, src, src_size);
378 }
379
TF_TString_Append(TF_TString * dst,const TF_TString * src)380 extern inline void TF_TString_Append(TF_TString *dst, const TF_TString *src) {
381 const char *src_c = TF_TString_GetDataPointer(src);
382 size_t size = TF_TString_GetSize(src);
383
384 TF_TString_AppendN(dst, src_c, size);
385 }
386
TF_TString_Copy(TF_TString * dst,const char * src,size_t size)387 extern inline void TF_TString_Copy(TF_TString *dst, const char *src,
388 size_t size) {
389 char *dst_c = TF_TString_ResizeUninitialized(dst, size);
390
391 if (size) memcpy(dst_c, src, size);
392 }
393
TF_TString_Assign(TF_TString * dst,const TF_TString * src)394 extern inline void TF_TString_Assign(TF_TString *dst, const TF_TString *src) {
395 if (dst == src) return;
396
397 TF_TString_Dealloc(dst);
398
399 switch (TF_TString_GetType(src)) {
400 case TF_TSTR_SMALL:
401 case TF_TSTR_VIEW:
402 *dst = *src;
403 return;
404 case TF_TSTR_LARGE: {
405 const char *src_c = TF_TString_GetDataPointer(src);
406 size_t size = TF_TString_GetSize(src);
407
408 TF_TString_Copy(dst, src_c, size);
409 }
410 return;
411 case TF_TSTR_OFFSET: {
412 const char *src_c = TF_TString_GetDataPointer(src);
413 size_t size = TF_TString_GetSize(src);
414
415 TF_TString_AssignView(dst, src_c, size);
416 }
417 return;
418 default:
419 return; // Unreachable.
420 }
421 }
422
TF_TString_Move(TF_TString * dst,TF_TString * src)423 extern inline void TF_TString_Move(TF_TString *dst, TF_TString *src) {
424 if (dst == src) return;
425
426 TF_TString_Dealloc(dst);
427
428 switch (TF_TString_GetType(src)) {
429 case TF_TSTR_SMALL:
430 case TF_TSTR_VIEW:
431 *dst = *src;
432 return;
433 case TF_TSTR_LARGE:
434 *dst = *src;
435 TF_TString_Init(src);
436 return;
437 case TF_TSTR_OFFSET: {
438 const char *src_c = TF_TString_GetDataPointer(src);
439 size_t size = TF_TString_GetSize(src);
440
441 TF_TString_AssignView(dst, src_c, size);
442 }
443 return;
444 default:
445 return; // Unreachable.
446 }
447 }
448
449 #endif // TENSORFLOW_CORE_PLATFORM_CTSTRING_INTERNAL_H_
450