1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md
4
5
6 #include <cstdio>
7 #include <cstdlib>
8 #include <string>
9
10 #include <OpenImageIO/Imath.h>
11 #include <OpenImageIO/dassert.h>
12 #include <OpenImageIO/strutil.h>
13 #include <OpenImageIO/typedesc.h>
14 #include <OpenImageIO/ustring.h>
15
16
17 OIIO_NAMESPACE_BEGIN
18
TypeDesc(string_view typestring)19 TypeDesc::TypeDesc(string_view typestring)
20 : basetype(UNKNOWN)
21 , aggregate(SCALAR)
22 , vecsemantics(NOXFORM)
23 , reserved(0)
24 , arraylen(0)
25 {
26 fromstring(typestring);
27 }
28
29
30
31 namespace {
32
33 static int basetype_size[TypeDesc::LASTBASE] = {
34 0, // UNKNOWN
35 0, // VOID
36 sizeof(unsigned char), // UCHAR
37 sizeof(char), // CHAR
38 sizeof(unsigned short), // USHORT
39 sizeof(short), // SHORT
40 sizeof(unsigned int), // UINT
41 sizeof(int), // INT
42 sizeof(unsigned long long), // ULONGLONG
43 sizeof(long long), // LONGLONG
44 sizeof(float) / 2, // HALF
45 sizeof(float), // FLOAT
46 sizeof(double), // DOUBLE
47 sizeof(char*), // STRING
48 sizeof(void*) // PTR
49 };
50
51 }
52
53 size_t
basesize() const54 TypeDesc::basesize() const noexcept
55 {
56 if (basetype >= TypeDesc::LASTBASE)
57 return 0;
58 OIIO_DASSERT(basetype < TypeDesc::LASTBASE);
59 return basetype_size[basetype];
60 }
61
62
63
64 bool
is_floating_point() const65 TypeDesc::is_floating_point() const noexcept
66 {
67 static bool isfloat[TypeDesc::LASTBASE] = {
68 0, // UNKNOWN
69 0, // VOID
70 0, // UCHAR
71 0, // CHAR
72 0, // USHORT
73 0, // SHORT
74 0, // UINT
75 0, // INT
76 0, // ULONGLONG
77 0, // LONGLONG
78 1, // HALF
79 1, // FLOAT
80 1, // DOUBLE
81 0, // STRING
82 0 // PTR
83 };
84 OIIO_DASSERT(basetype < TypeDesc::LASTBASE);
85 return isfloat[basetype];
86 }
87
88
89
90 bool
is_signed() const91 TypeDesc::is_signed() const noexcept
92 {
93 static bool issigned[TypeDesc::LASTBASE] = {
94 0, // UNKNOWN
95 0, // VOID
96 0, // UCHAR
97 1, // CHAR
98 0, // USHORT
99 1, // SHORT
100 0, // UINT
101 1, // INT
102 0, // ULONGLONG
103 1, // LONGLONG
104 1, // HALF
105 1, // FLOAT
106 1, // DOUBLE
107 0, // STRING
108 0 // PTR
109 };
110 OIIO_DASSERT(basetype < TypeDesc::LASTBASE);
111 return issigned[basetype];
112 }
113
114
115
116 namespace {
117
118 static const char* basetype_name[] = {
119 "unknown", // UNKNOWN
120 "void", // VOID/NONE
121 "uint8", // UCHAR
122 "int8", // CHAR
123 "uint16", // USHORT
124 "int16", // SHORT
125 "uint", // UINT
126 "int", // INT
127 "uint64", // ULONGLONG
128 "int64", // LONGLONG
129 "half", // HALF
130 "float", // FLOAT
131 "double", // DOUBLE
132 "string", // STRING
133 "pointer" // PTR
134 };
135
136 static const char* basetype_code[] = {
137 "unknown", // UNKNOWN
138 "void", // VOID/NONE
139 "uc", // UCHAR
140 "c", // CHAR
141 "us", // USHORT
142 "s", // SHORT
143 "ui", // UINT
144 "i", // INT
145 "ull", // ULONGLONG
146 "ll", // LONGLONG
147 "h", // HALF
148 "f", // FLOAT
149 "d", // DOUBLE
150 "str", // STRING
151 "ptr" // PTR
152 };
153
154 } // namespace
155
156 const char*
c_str() const157 TypeDesc::c_str() const
158 {
159 // FIXME : how about a per-thread cache of the last one or two, so
160 // we don't have to re-assemble strings all the time?
161
162 // Timecode and Keycode are hard coded
163 static constexpr TypeDesc TypeTimeCodeAlt(UINT, VEC2, TIMECODE);
164 if (*this == TypeTimeCode || *this == TypeTimeCodeAlt)
165 return ustring("timecode").c_str();
166 else if (*this == TypeKeyCode)
167 return ustring("keycode").c_str();
168
169 std::string result;
170 if (aggregate == SCALAR)
171 result = basetype_name[basetype];
172 // else if (aggregate == MATRIX44 && basetype == FLOAT)
173 // result = "matrix";
174 // else if (aggregate == MATRIX33 && basetype == FLOAT)
175 // result = "matrix33";
176 // else if (aggregate == VEC2 && basetype == FLOAT && vecsemantics == NOXFORM)
177 // result = "float2";
178 // else if (aggregate == VEC4 && basetype == FLOAT && vecsemantics == NOXFORM)
179 // result = "float4";
180 else if (vecsemantics == NOXFORM) {
181 switch (aggregate) {
182 case VEC2: result = "float2"; break;
183 case VEC3: result = "float3"; break;
184 case VEC4: result = "float4"; break;
185 case MATRIX33: result = "matrix33"; break;
186 case MATRIX44: result = "matrix"; break;
187 }
188 if (basetype != FLOAT)
189 result += basetype_code[basetype];
190 } else {
191 // Special names for vector semantics
192 const char* vec = "";
193 switch (vecsemantics) {
194 case COLOR: vec = "color"; break;
195 case POINT: vec = "point"; break;
196 case VECTOR: vec = "vector"; break;
197 case NORMAL: vec = "normal"; break;
198 case RATIONAL: vec = "rational"; break;
199 default: OIIO_DASSERT(0 && "Invalid vector semantics");
200 }
201 const char* agg = "";
202 switch (aggregate) {
203 case VEC2: agg = "2"; break;
204 case VEC4: agg = "4"; break;
205 case MATRIX33: agg = "matrix33"; break;
206 case MATRIX44: agg = "matrix44"; break;
207 }
208 result = std::string(vec) + std::string(agg);
209 if (basetype != FLOAT)
210 result += basetype_code[basetype];
211 }
212 if (arraylen > 0)
213 result += Strutil::sprintf("[%d]", arraylen);
214 else if (arraylen < 0)
215 result += "[]";
216 return ustring(result).c_str();
217 }
218
219
220
221 // Copy src into dst until you hit the end, find a delimiter charcter,
222 // or have copied maxlen-1 characters, whichever comes first. Add a
223 // terminating null charcter. Return the number of characters copied.
224 inline size_t
copy_until(const char * src,const char * delim,char * dst,size_t maxlen)225 copy_until(const char* src, const char* delim, char* dst, size_t maxlen)
226 {
227 size_t i = 0;
228 while (src[i] && i < maxlen - 1) {
229 bool found_delim = false;
230 for (int d = 0; delim[d]; ++d)
231 if (src[i] == delim[d])
232 found_delim = true;
233 if (found_delim)
234 break;
235 dst[i] = src[i];
236 ++i;
237 }
238 dst[i] = 0;
239 return i;
240 }
241
242
243
244 size_t
fromstring(string_view typestring)245 TypeDesc::fromstring(string_view typestring)
246 {
247 *this = TypeDesc::UNKNOWN;
248 string_view orig = typestring;
249 if (typestring.empty()) {
250 return 0;
251 }
252
253 // The first "word" should be a type name.
254 string_view type = Strutil::parse_identifier(typestring);
255
256 // Check the scalar types in our table above
257 TypeDesc t;
258 for (int i = 0; i < LASTBASE; ++i) {
259 if (type == basetype_name[i]) {
260 t.basetype = i;
261 break;
262 }
263 }
264
265 // Some special case names for aggregates
266 if (t.basetype != UNKNOWN) {
267 // already solved
268 } else if (type == "color")
269 t = TypeColor;
270 else if (type == "point")
271 t = TypePoint;
272 else if (type == "vector")
273 t = TypeVector;
274 else if (type == "normal")
275 t = TypeNormal;
276 else if (type == "matrix33")
277 t = TypeMatrix33;
278 else if (type == "matrix" || type == "matrix44")
279 t = TypeMatrix44;
280 else if (type == "vector2")
281 t = TypeVector2;
282 else if (type == "vector4")
283 t = TypeVector4;
284 else if (type == "timecode")
285 t = TypeTimeCode;
286 else if (type == "rational")
287 t = TypeRational;
288 else {
289 return 0; // unknown
290 }
291
292 // Is there an array length following the type name?
293 if (Strutil::parse_char(typestring, '[')) {
294 int arraylen = -1;
295 Strutil::parse_int(typestring, arraylen);
296 if (!Strutil::parse_char(typestring, ']'))
297 return 0; // malformed
298 t.arraylen = arraylen;
299 }
300
301 *this = t;
302 return orig.length() - typestring.length();
303 }
304
305
306
tostring_formatting(const char * int_fmt,const char * float_fmt,const char * string_fmt,const char * ptr_fmt,const char * aggregate_begin,const char * aggregate_end,const char * aggregate_sep,const char * array_begin,const char * array_end,const char * array_sep,int flags,const char * uint_fmt)307 tostring_formatting::tostring_formatting(
308 const char* int_fmt, const char* float_fmt, const char* string_fmt,
309 const char* ptr_fmt, const char* aggregate_begin, const char* aggregate_end,
310 const char* aggregate_sep, const char* array_begin, const char* array_end,
311 const char* array_sep, int flags, const char* uint_fmt)
312 : int_fmt(int_fmt)
313 , float_fmt(float_fmt)
314 , string_fmt(string_fmt)
315 , ptr_fmt(ptr_fmt)
316 , aggregate_begin(aggregate_begin)
317 , aggregate_end(aggregate_end)
318 , aggregate_sep(aggregate_sep)
319 , array_begin(array_begin)
320 , array_end(array_end)
321 , array_sep(array_sep)
322 , flags(flags)
323 , uint_fmt(uint_fmt)
324 {
325 }
326
327
328
tostring_formatting(Notation notation,const char * int_fmt,const char * uint_fmt,const char * float_fmt,const char * string_fmt,const char * ptr_fmt,const char * aggregate_begin,const char * aggregate_end,const char * aggregate_sep,const char * array_begin,const char * array_end,const char * array_sep,int flags)329 tostring_formatting::tostring_formatting(
330 Notation notation, const char* int_fmt, const char* uint_fmt,
331 const char* float_fmt, const char* string_fmt, const char* ptr_fmt,
332 const char* aggregate_begin, const char* aggregate_end,
333 const char* aggregate_sep, const char* array_begin, const char* array_end,
334 const char* array_sep, int flags)
335 : tostring_formatting(int_fmt, float_fmt, string_fmt, ptr_fmt,
336 aggregate_begin, aggregate_end, aggregate_sep,
337 array_begin, array_end, array_sep, flags, uint_fmt)
338 {
339 use_sprintf = false;
340 }
341
342
343
344 template<class T>
345 static std::string
sprint_type(TypeDesc type,const char * format,const tostring_formatting & fmt,const T * v)346 sprint_type(TypeDesc type, const char* format, const tostring_formatting& fmt,
347 const T* v)
348 {
349 std::string val;
350 if (type.arraylen)
351 val += fmt.array_begin;
352 const size_t n = type.arraylen ? type.arraylen : 1;
353 for (size_t i = 0; i < n; ++i) {
354 if (type.aggregate > 1)
355 val += fmt.aggregate_begin;
356 for (int j = 0; j < (int)type.aggregate; ++j, ++v) {
357 val += Strutil::sprintf(format, *v);
358 if (type.aggregate > 1 && j < type.aggregate - 1)
359 val += fmt.aggregate_sep;
360 }
361 if (type.aggregate > 1)
362 val += fmt.aggregate_end;
363 if (i < n - 1)
364 val += fmt.array_sep;
365 }
366 if (type.arraylen)
367 val += fmt.array_end;
368 return val;
369 }
370
371
372
373 static std::string
sprint_type(TypeDesc type,const char * format,const tostring_formatting & fmt,const char ** v)374 sprint_type(TypeDesc type, const char* format, const tostring_formatting& fmt,
375 const char** v)
376 {
377 std::string val;
378 if (type.arraylen)
379 val += fmt.array_begin;
380 const size_t n = type.arraylen ? type.arraylen : 1;
381 for (size_t i = 0; i < n; ++i) {
382 if (type.aggregate > 1)
383 val += fmt.aggregate_begin;
384 for (int j = 0; j < (int)type.aggregate; ++j, ++v) {
385 if (fmt.flags & tostring_formatting::escape_strings)
386 val += Strutil::sprintf(format, *v ? Strutil::escape_chars(*v)
387 : std::string());
388 else
389 val += Strutil::sprintf(format, *v ? *v : "");
390 if (type.aggregate > 1 && j < type.aggregate - 1)
391 val += fmt.aggregate_sep;
392 }
393 if (type.aggregate > 1)
394 val += fmt.aggregate_end;
395 if (i < n - 1)
396 val += fmt.array_sep;
397 }
398 if (type.arraylen)
399 val += fmt.array_end;
400 return val;
401 }
402
403
404
405 template<class T>
406 static std::string
format_type(TypeDesc type,const char * format,const tostring_formatting & fmt,const T * v)407 format_type(TypeDesc type, const char* format, const tostring_formatting& fmt,
408 const T* v)
409 {
410 std::string val;
411 if (type.arraylen)
412 val += fmt.array_begin;
413 const size_t n = type.arraylen ? type.arraylen : 1;
414 for (size_t i = 0; i < n; ++i) {
415 if (type.aggregate > 1)
416 val += fmt.aggregate_begin;
417 for (int j = 0; j < (int)type.aggregate; ++j, ++v) {
418 val += Strutil::fmt::format(format, *v);
419 if (type.aggregate > 1 && j < type.aggregate - 1)
420 val += fmt.aggregate_sep;
421 }
422 if (type.aggregate > 1)
423 val += fmt.aggregate_end;
424 if (i < n - 1)
425 val += fmt.array_sep;
426 }
427 if (type.arraylen)
428 val += fmt.array_end;
429 return val;
430 }
431
432
433
434 static std::string
format_type(TypeDesc type,const char * format,const tostring_formatting & fmt,const char ** v)435 format_type(TypeDesc type, const char* format, const tostring_formatting& fmt,
436 const char** v)
437 {
438 std::string val;
439 if (type.arraylen)
440 val += fmt.array_begin;
441 const size_t n = type.arraylen ? type.arraylen : 1;
442 for (size_t i = 0; i < n; ++i) {
443 if (type.aggregate > 1)
444 val += fmt.aggregate_begin;
445 for (int j = 0; j < (int)type.aggregate; ++j, ++v) {
446 if (fmt.flags & tostring_formatting::escape_strings)
447 val += Strutil::fmt::format(format,
448 *v ? Strutil::escape_chars(*v)
449 : std::string());
450 else
451 val += Strutil::fmt::format(format, *v ? *v : "");
452 if (type.aggregate > 1 && j < type.aggregate - 1)
453 val += fmt.aggregate_sep;
454 }
455 if (type.aggregate > 1)
456 val += fmt.aggregate_end;
457 if (i < n - 1)
458 val += fmt.array_sep;
459 }
460 if (type.arraylen)
461 val += fmt.array_end;
462 return val;
463 }
464
465
466
467 // From OpenEXR
468 inline unsigned int
bitField(unsigned int value,int minBit,int maxBit)469 bitField(unsigned int value, int minBit, int maxBit)
470 {
471 int shift = minBit;
472 unsigned int mask = (~(~0U << (maxBit - minBit + 1)) << minBit);
473 return (value & mask) >> shift;
474 }
475
476
477 // From OpenEXR
478 inline int
bcdToBinary(unsigned int bcd)479 bcdToBinary(unsigned int bcd)
480 {
481 return int((bcd & 0x0f) + 10 * ((bcd >> 4) & 0x0f));
482 }
483
484
485
486 std::string
tostring(TypeDesc type,const void * data,const tostring_formatting & fmt)487 tostring(TypeDesc type, const void* data, const tostring_formatting& fmt)
488 {
489 // Perhaps there is a way to use CType<> with a dynamic argument?
490 switch (type.basetype) {
491 case TypeDesc::UNKNOWN:
492 return fmt.use_sprintf
493 ? sprint_type(type, fmt.ptr_fmt, fmt, (void**)data)
494 : format_type(type, fmt.ptr_fmt, fmt, (void**)data);
495 case TypeDesc::NONE:
496 return fmt.use_sprintf ? sprint_type(type, "None", fmt, (void**)data)
497 : format_type(type, "None", fmt, (void**)data);
498 case TypeDesc::UCHAR:
499 return fmt.use_sprintf
500 ? sprint_type(type, fmt.uint_fmt ? fmt.uint_fmt : "%u", fmt,
501 (unsigned char*)data)
502 : format_type(type, fmt.uint_fmt ? fmt.uint_fmt : "%u", fmt,
503 (unsigned char*)data);
504 case TypeDesc::CHAR:
505 return fmt.use_sprintf
506 ? sprint_type(type, fmt.int_fmt, fmt, (char*)data)
507 : format_type(type, fmt.int_fmt, fmt, (char*)data);
508 case TypeDesc::USHORT:
509 return fmt.use_sprintf
510 ? sprint_type(type, fmt.uint_fmt ? fmt.uint_fmt : "%u", fmt,
511 (uint16_t*)data)
512 : format_type(type, fmt.uint_fmt ? fmt.uint_fmt : "%u", fmt,
513 (uint16_t*)data);
514 case TypeDesc::SHORT:
515 return fmt.use_sprintf
516 ? sprint_type(type, fmt.int_fmt, fmt, (short*)data)
517 : format_type(type, fmt.int_fmt, fmt, (short*)data);
518 case TypeDesc::UINT:
519 if (type.vecsemantics == TypeDesc::RATIONAL
520 && type.aggregate == TypeDesc::VEC2) {
521 std::string out;
522 const uint32_t* val = (const uint32_t*)data;
523 for (size_t i = 0, e = type.numelements(); i < e; ++i, val += 2) {
524 if (i)
525 out += ", ";
526 out += Strutil::sprintf("%u/%u", val[0], val[1]);
527 }
528 return out;
529 } else if (type == TypeTimeCode) {
530 // Replicating the logic in OpenEXR, but this prevents us from
531 // needing to link to libIlmImf just to do this.
532 unsigned int t = *(unsigned int*)data;
533 int hours = bcdToBinary(bitField(t, 24, 29));
534 int minutes = bcdToBinary(bitField(t, 16, 22));
535 int seconds = bcdToBinary(bitField(t, 8, 14));
536 int frame = bcdToBinary(bitField(t, 0, 5));
537 return Strutil::sprintf("%02d:%02d:%02d:%02d", hours, minutes,
538 seconds, frame);
539 }
540 return fmt.use_sprintf
541 ? sprint_type(type, fmt.uint_fmt ? fmt.uint_fmt : "%u", fmt,
542 (unsigned int*)data)
543 : format_type(type, fmt.uint_fmt ? fmt.uint_fmt : "%u", fmt,
544 (unsigned int*)data);
545 case TypeDesc::INT:
546 if (type.elementtype() == TypeRational) {
547 std::string out;
548 const int* val = (const int*)data;
549 for (size_t i = 0, e = type.numelements(); i < e; ++i, val += 2) {
550 if (i)
551 out += ", ";
552 out += Strutil::sprintf("%d/%d", val[0], val[1]);
553 }
554 return out;
555 }
556 return fmt.use_sprintf
557 ? sprint_type(type, fmt.int_fmt, fmt, (int*)data)
558 : format_type(type, fmt.int_fmt, fmt, (int*)data);
559 case TypeDesc::UINT64:
560 return fmt.use_sprintf
561 ? sprint_type(type, fmt.uint_fmt ? fmt.uint_fmt : "%u", fmt,
562 (const uint64_t*)data)
563 : format_type(type, fmt.uint_fmt ? fmt.uint_fmt : "%u", fmt,
564 (const uint64_t*)data);
565 case TypeDesc::INT64:
566 return fmt.use_sprintf
567 ? sprint_type(type, fmt.int_fmt, fmt, (const int64_t*)data)
568 : format_type(type, fmt.int_fmt, fmt, (const int64_t*)data);
569 case TypeDesc::HALF:
570 return fmt.use_sprintf
571 ? sprint_type(type, fmt.float_fmt, fmt, (const half*)data)
572 : format_type(type, fmt.float_fmt, fmt, (const half*)data);
573 case TypeDesc::FLOAT:
574 return fmt.use_sprintf
575 ? sprint_type(type, fmt.float_fmt, fmt, (const float*)data)
576 : format_type(type, fmt.float_fmt, fmt, (const float*)data);
577 case TypeDesc::DOUBLE:
578 return fmt.use_sprintf
579 ? sprint_type(type, fmt.float_fmt, fmt, (const double*)data)
580 : format_type(type, fmt.float_fmt, fmt, (const double*)data);
581 case TypeDesc::STRING:
582 if (!type.is_array()
583 && !(fmt.flags & tostring_formatting::quote_single_string))
584 return *(const char**)data;
585 return fmt.use_sprintf
586 ? sprint_type(type, fmt.string_fmt, fmt, (const char**)data)
587 : format_type(type, fmt.string_fmt, fmt, (const char**)data);
588 case TypeDesc::PTR:
589 return fmt.use_sprintf
590 ? sprint_type(type, fmt.ptr_fmt, fmt, (void**)data)
591 : format_type(type, fmt.ptr_fmt, fmt, (void**)data);
592 default:
593 #ifndef NDEBUG
594 return Strutil::sprintf("<unknown data type> (base %d, agg %d vec %d)",
595 type.basetype, type.aggregate,
596 type.vecsemantics);
597 #endif
598 break;
599 }
600 return "";
601 }
602
603
604
605 // Old deprecated one
606 std::string
tostring(TypeDesc type,const void * data,const char * float_fmt,const char * string_fmt,const char aggregate_delim[2],const char * aggregate_sep,const char array_delim[2],const char * array_sep)607 tostring(TypeDesc type, const void* data, const char* float_fmt,
608 const char* string_fmt, const char aggregate_delim[2],
609 const char* aggregate_sep, const char array_delim[2],
610 const char* array_sep)
611 {
612 tostring_formatting fmt("%d", float_fmt, string_fmt, "%p",
613 std::string(aggregate_delim + 0, 1).c_str(),
614 std::string(aggregate_delim + 1, 1).c_str(),
615 aggregate_sep,
616 std::string(array_delim + 0, 1).c_str(),
617 std::string(array_delim + 1, 1).c_str(), array_sep);
618 return tostring(type, data, fmt);
619 }
620
621
622
623 namespace {
624
625 template<typename T = int>
626 static bool
to_ints(TypeDesc srctype,const void * src,T * dst,size_t n=1)627 to_ints(TypeDesc srctype, const void* src, T* dst, size_t n = 1)
628 {
629 if (srctype.basetype == TypeDesc::UINT) {
630 for (size_t i = 0; i < n; ++i)
631 dst[i] = T(((const unsigned int*)src)[i]);
632 return true;
633 }
634 if (srctype.basetype == TypeDesc::INT16) {
635 for (size_t i = 0; i < n; ++i)
636 dst[i] = T(((const short*)src)[i]);
637 return true;
638 }
639 if (srctype.basetype == TypeDesc::UINT16) {
640 for (size_t i = 0; i < n; ++i)
641 dst[i] = T(((const unsigned short*)src)[i]);
642 return true;
643 }
644 if (srctype.basetype == TypeDesc::INT8) {
645 for (size_t i = 0; i < n; ++i)
646 dst[i] = T(((const char*)src)[i]);
647 return true;
648 }
649 if (srctype.basetype == TypeDesc::UINT8) {
650 for (size_t i = 0; i < n; ++i)
651 dst[i] = T(((const unsigned char*)src)[i]);
652 return true;
653 }
654 if (srctype.basetype == TypeDesc::INT64) {
655 for (size_t i = 0; i < n; ++i)
656 dst[i] = T(((const long long*)src)[i]);
657 return true;
658 }
659 if (srctype.basetype == TypeDesc::UINT64) {
660 for (size_t i = 0; i < n; ++i)
661 dst[i] = T(((const unsigned long long*)src)[i]);
662 return true;
663 }
664 return false;
665 }
666
667
668 template<typename T = float>
669 static bool
to_floats(TypeDesc srctype,const void * src,T * dst,size_t n=1)670 to_floats(TypeDesc srctype, const void* src, T* dst, size_t n = 1)
671 {
672 if (srctype.basetype == TypeDesc::FLOAT) {
673 for (size_t i = 0; i < n; ++i)
674 dst[i] = T(((const float*)src)[i]);
675 return true;
676 }
677 if (srctype.basetype == TypeDesc::HALF) {
678 for (size_t i = 0; i < n; ++i)
679 dst[i] = T(((const half*)src)[i]);
680 return true;
681 }
682 if (srctype.basetype == TypeDesc::DOUBLE) {
683 for (size_t i = 0; i < n; ++i)
684 dst[i] = T(((const double*)src)[i]);
685 return true;
686 }
687 if (srctype.basetype == TypeDesc::UINT) {
688 for (size_t i = 0; i < n; ++i)
689 dst[i] = T(((const unsigned int*)src)[i]);
690 return true;
691 }
692 if (srctype.basetype == TypeDesc::INT16) {
693 for (size_t i = 0; i < n; ++i)
694 dst[i] = T(((const short*)src)[i]);
695 return true;
696 }
697 if (srctype.basetype == TypeDesc::UINT16) {
698 for (size_t i = 0; i < n; ++i)
699 dst[i] = T(((const unsigned short*)src)[i]);
700 return true;
701 }
702 if (srctype.basetype == TypeDesc::INT8) {
703 for (size_t i = 0; i < n; ++i)
704 dst[i] = T(((const char*)src)[i]);
705 return true;
706 }
707 if (srctype.basetype == TypeDesc::UINT8) {
708 for (size_t i = 0; i < n; ++i)
709 dst[i] = T(((const unsigned char*)src)[i]);
710 return true;
711 }
712 if (srctype.basetype == TypeDesc::INT64) {
713 for (size_t i = 0; i < n; ++i)
714 dst[i] = T(((const long long*)src)[i]);
715 return true;
716 }
717 if (srctype.basetype == TypeDesc::UINT64) {
718 for (size_t i = 0; i < n; ++i)
719 dst[i] = T(((const unsigned long long*)src)[i]);
720 return true;
721 }
722 return false;
723 }
724
725 } // namespace
726
727
728
729 bool
convert_type(TypeDesc srctype,const void * src,TypeDesc dsttype,void * dst,int n)730 convert_type(TypeDesc srctype, const void* src, TypeDesc dsttype, void* dst,
731 int n)
732 {
733 if (n > 1) {
734 // Handle multiple values by turning into or expanding array length
735 srctype.arraylen = srctype.numelements() * n;
736 dsttype.arraylen = dsttype.numelements() * n;
737 }
738
739 if (srctype.basetype == dsttype.basetype
740 && srctype.basevalues() == dsttype.basevalues()) {
741 size_t size = srctype.size();
742 memcpy(dst, src, size);
743 return size;
744 }
745
746 if (dsttype == TypeString) {
747 (*(ustring*)dst) = ustring(tostring(srctype, src));
748 return true;
749 }
750
751 if (dsttype.basetype == TypeDesc::INT
752 && dsttype.basevalues() == srctype.basevalues()) {
753 if (to_ints<int>(srctype, src, (int*)dst, dsttype.basevalues()))
754 return true;
755 }
756 if (dsttype == TypeInt && srctype == TypeString) {
757 // Only succeed for a string if it exactly holds something that
758 // excatly parses to an int value.
759 string_view str(((const char**)src)[0]);
760 int val = 0;
761 if (Strutil::parse_int(str, val) && str.empty()) {
762 ((int*)dst)[0] = val;
763 return true;
764 }
765 }
766 if (dsttype.basetype == TypeDesc::UINT
767 && dsttype.basevalues() == srctype.basevalues()) {
768 if (to_ints<uint32_t>(srctype, src, (uint32_t*)dst,
769 dsttype.basevalues()))
770 return true;
771 }
772 // N.B. No uint inversion from string
773
774 if (dsttype.basetype == TypeDesc::FLOAT
775 && dsttype.basevalues() == srctype.basevalues()) {
776 if (to_floats<float>(srctype, src, (float*)dst, dsttype.basevalues()))
777 return true;
778 }
779 if (dsttype == TypeFloat && srctype == TypeRational) {
780 int num = ((const int*)src)[0];
781 int den = ((const int*)src)[1];
782 ((float*)dst)[0] = den ? float(num) / float(den) : 0.0f;
783 return true;
784 }
785 if (dsttype == TypeFloat && srctype == TypeString) {
786 // Only succeed for a string if it exactly holds something that
787 // excatly parses to a float value.
788 string_view str(((const char**)src)[0]);
789 float val = 0;
790 if (Strutil::parse_float(str, val) && str.empty()) {
791 ((float*)dst)[0] = val;
792 return true;
793 }
794 }
795
796 if (dsttype.basetype == TypeDesc::DOUBLE
797 && dsttype.basevalues() == srctype.basevalues()) {
798 if (to_floats<double>(srctype, src, (double*)dst, dsttype.basevalues()))
799 return true;
800 }
801 return false;
802 }
803
804
805
806 bool
operator <(const TypeDesc & x) const807 TypeDesc::operator<(const TypeDesc& x) const noexcept
808 {
809 if (basetype != x.basetype)
810 return basetype < x.basetype;
811 if (aggregate != x.aggregate)
812 return aggregate < x.aggregate;
813 if (arraylen != x.arraylen)
814 return arraylen < x.arraylen;
815 if (vecsemantics != x.vecsemantics)
816 return vecsemantics < x.vecsemantics;
817 return false; // they are equal
818 }
819
820
821
822 TypeDesc::BASETYPE
basetype_merge(TypeDesc at,TypeDesc bt)823 TypeDesc::basetype_merge(TypeDesc at, TypeDesc bt)
824 {
825 BASETYPE a = (BASETYPE)at.basetype;
826 BASETYPE b = (BASETYPE)bt.basetype;
827
828 // Same type already? done.
829 if (a == b)
830 return a;
831 if (a == UNKNOWN)
832 return b;
833 if (b == UNKNOWN)
834 return a;
835 // Canonicalize so a's size (in bytes) is >= b's size in bytes. This
836 // unclutters remaining cases.
837 if (TypeDesc(a).size() < TypeDesc(b).size())
838 std::swap(a, b);
839 // Double or float trump anything else
840 if (a == DOUBLE || a == FLOAT)
841 return a;
842 if (a == UINT32 && (b == UINT16 || b == UINT8))
843 return a;
844 if (a == INT32 && (b == INT16 || b == UINT16 || b == INT8 || b == UINT8))
845 return a;
846 if ((a == UINT16 || a == HALF) && b == UINT8)
847 return a;
848 if ((a == INT16 || a == HALF) && (b == INT8 || b == UINT8))
849 return a;
850 // Out of common cases. For all remaining edge cases, punt and say that
851 // we prefer float.
852 return FLOAT;
853 }
854
855
856
857 const TypeDesc TypeDesc::TypeFloat(TypeDesc::FLOAT);
858 const TypeDesc TypeDesc::TypeColor(TypeDesc::FLOAT, TypeDesc::VEC3,
859 TypeDesc::COLOR);
860 const TypeDesc TypeDesc::TypePoint(TypeDesc::FLOAT, TypeDesc::VEC3,
861 TypeDesc::POINT);
862 const TypeDesc TypeDesc::TypeVector(TypeDesc::FLOAT, TypeDesc::VEC3,
863 TypeDesc::VECTOR);
864 const TypeDesc TypeDesc::TypeNormal(TypeDesc::FLOAT, TypeDesc::VEC3,
865 TypeDesc::NORMAL);
866 const TypeDesc TypeDesc::TypeMatrix33(TypeDesc::FLOAT, TypeDesc::MATRIX33);
867 const TypeDesc TypeDesc::TypeMatrix44(TypeDesc::FLOAT, TypeDesc::MATRIX44);
868 const TypeDesc TypeDesc::TypeMatrix = TypeDesc::TypeMatrix44;
869 const TypeDesc TypeDesc::TypeString(TypeDesc::STRING);
870 const TypeDesc TypeDesc::TypeInt(TypeDesc::INT);
871 const TypeDesc TypeDesc::TypeHalf(TypeDesc::HALF);
872 const TypeDesc TypeDesc::TypeTimeCode(TypeDesc::UINT, TypeDesc::SCALAR,
873 TypeDesc::TIMECODE, 2);
874 const TypeDesc TypeDesc::TypeKeyCode(TypeDesc::INT, TypeDesc::SCALAR,
875 TypeDesc::KEYCODE, 7);
876 const TypeDesc TypeDesc::TypeFloat4(TypeDesc::FLOAT, TypeDesc::VEC4);
877 const TypeDesc TypeDesc::TypeRational(TypeDesc::INT, TypeDesc::VEC2,
878 TypeDesc::RATIONAL);
879
880
881 OIIO_NAMESPACE_END
882