• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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