• 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 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