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 static 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 #if 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 #if 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 static inline void TF_TString_Init(TF_TString *str) {
172 memset(str->u.raw.raw, 0, sizeof(TF_TString_Raw));
173 }
174
TF_TString_Dealloc(TF_TString * str)175 static inline void TF_TString_Dealloc(TF_TString *str) {
176 if (TF_TString_GetType(str) == TF_TSTR_LARGE &&
177 str->u.large.ptr != NULL) { // NOLINT
178 free(str->u.large.ptr);
179 TF_TString_Init(str);
180 }
181 }
182
TF_TString_GetSize(const TF_TString * str)183 static inline size_t TF_TString_GetSize(const TF_TString *str) {
184 switch (TF_TString_GetType(str)) {
185 case TF_TSTR_SMALL:
186 return str->u.smll.size >> 2;
187 case TF_TSTR_LARGE:
188 return TF_TString_ToActualSizeT(str->u.large.size);
189 case TF_TSTR_OFFSET:
190 return TF_le32toh(str->u.offset.size) >> 2;
191 case TF_TSTR_VIEW:
192 return TF_TString_ToActualSizeT(str->u.view.size);
193 default:
194 return 0; // Unreachable.
195 }
196 }
197
TF_TString_GetCapacity(const TF_TString * str)198 static inline size_t TF_TString_GetCapacity(const TF_TString *str) {
199 switch (TF_TString_GetType(str)) {
200 case TF_TSTR_SMALL:
201 return TF_TString_SmallCapacity;
202 case TF_TSTR_LARGE:
203 return str->u.large.cap;
204 case TF_TSTR_OFFSET:
205 case TF_TSTR_VIEW:
206 default:
207 return 0;
208 }
209 }
210
TF_TString_GetDataPointer(const TF_TString * str)211 static inline const char *TF_TString_GetDataPointer(const TF_TString *str) {
212 switch (TF_TString_GetType(str)) {
213 case TF_TSTR_SMALL:
214 return str->u.smll.str;
215 case TF_TSTR_LARGE:
216 return str->u.large.ptr;
217 case TF_TSTR_OFFSET:
218 return (const char *)str + str->u.offset.offset; // NOLINT
219 case TF_TSTR_VIEW:
220 return str->u.view.ptr;
221 default:
222 // Unreachable.
223 return NULL; // NOLINT
224 }
225 }
226
TF_TString_ResizeUninitialized(TF_TString * str,size_t new_size)227 static inline char *TF_TString_ResizeUninitialized(TF_TString *str,
228 size_t new_size) {
229 size_t curr_size = TF_TString_GetSize(str);
230 size_t copy_size = TF_min(new_size, curr_size);
231
232 TF_TString_Type curr_type = TF_TString_GetType(str);
233 const char *curr_ptr = TF_TString_GetDataPointer(str);
234
235 // Case: SMALL/LARGE/VIEW/OFFSET -> SMALL
236 if (new_size <= TF_TString_SmallCapacity) {
237 str->u.smll.size = (uint8_t)((new_size << 2) | TF_TSTR_SMALL); // NOLINT
238 str->u.smll.str[new_size] = '\0';
239
240 if (curr_type != TF_TSTR_SMALL && copy_size) {
241 memcpy(str->u.smll.str, curr_ptr, copy_size);
242 }
243
244 if (curr_type == TF_TSTR_LARGE) {
245 free((void *)curr_ptr); // NOLINT
246 }
247
248 // We do not clear out the newly excluded region.
249
250 return str->u.smll.str;
251 }
252
253 // Case: SMALL/LARGE/VIEW/OFFSET -> LARGE
254 size_t new_cap;
255 size_t curr_cap = TF_TString_GetCapacity(str);
256 // We assume SIZE_MAX % 16 == 0.
257 size_t curr_cap_x2 = curr_cap >= SIZE_MAX / 2 ? SIZE_MAX - 1 : curr_cap * 2;
258
259 if (new_size < curr_size && new_size < curr_cap / 2) {
260 // TODO(dero): Replace with shrink_to_fit flag.
261 new_cap = TF_align16(curr_cap / 2 + 1) - 1;
262 } else if (new_size > curr_cap_x2) {
263 new_cap = TF_align16(new_size + 1) - 1;
264 } else if (new_size > curr_cap) {
265 new_cap = TF_align16(curr_cap_x2 + 1) - 1;
266 } else {
267 new_cap = curr_cap;
268 }
269
270 char *new_ptr;
271 if (new_cap == curr_cap) {
272 new_ptr = str->u.large.ptr;
273 } else if (curr_type == TF_TSTR_LARGE) {
274 new_ptr = (char *)realloc(str->u.large.ptr, new_cap + 1); // NOLINT
275 } else {
276 new_ptr = (char *)malloc(new_cap + 1); // NOLINT
277 if (copy_size) {
278 memcpy(new_ptr, curr_ptr, copy_size);
279 }
280 }
281
282 str->u.large.size = TF_TString_ToInternalSizeT(new_size, TF_TSTR_LARGE);
283 str->u.large.ptr = new_ptr;
284 str->u.large.ptr[new_size] = '\0';
285 str->u.large.cap = new_cap;
286
287 return str->u.large.ptr;
288 }
289
TF_TString_GetMutableDataPointer(TF_TString * str)290 static inline char *TF_TString_GetMutableDataPointer(TF_TString *str) {
291 switch (TF_TString_GetType(str)) {
292 case TF_TSTR_SMALL:
293 return str->u.smll.str;
294 case TF_TSTR_OFFSET:
295 case TF_TSTR_VIEW:
296 // Convert OFFSET/VIEW to SMALL/LARGE
297 TF_TString_ResizeUninitialized(str, TF_TString_GetSize(str));
298 return (TF_TString_GetType(str) == TF_TSTR_SMALL) ? str->u.smll.str
299 : 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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