yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
4d517794e
master
1#include "slang-json-native.h" 2 3#include "../core/slang-rtti-util.h" 4#include "slang-com-helper.h" 5#include "slang-json-diagnostics.h" 6 7namespace Slang 8{ 9 10/* static */ RttiTypeFuncsMap JSONNativeUtil ::getTypeFuncsMap () 11{ 12RttiTypeFuncsMap typeMap ; 13typeMap .add (GetRttiInfo < JSONValue > ::get (),GetRttiTypeFuncsForZeroPod < JSONValue > ::getFuncs ()); 14return typeMap ; 15} 16 17/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! JSONToNativeConverter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ 18 19/* static */ Index JSONToNativeConverter ::_getFieldCount (const StructRttiInfo * structRttiInfo ) 20{ 21if (structRttiInfo -> m_super ) 22 { 23return _getFieldCount (structRttiInfo -> m_super )+ structRttiInfo -> m_fieldCount ; 24 } 25else 26 { 27return structRttiInfo -> m_fieldCount ; 28 } 29} 30 31/* static */ Index JSONToNativeConverter ::_findFieldIndex ( 32const StructRttiInfo * structRttiInfo , 33const UnownedStringSlice & fieldName ) 34{ 35if (structRttiInfo -> m_super ) 36 { 37const Index index = _findFieldIndex (structRttiInfo -> m_super ,fieldName ); 38if (index >=0 ) 39 { 40return index + _getFieldCount (structRttiInfo -> m_super ); 41 } 42 } 43 44ConstArrayView < StructRttiInfo ::Field > fields ( 45structRttiInfo -> m_fields , 46structRttiInfo -> m_fieldCount ); 47 48Index index = fields .findFirstIndex ( 49 [fieldName ](const StructRttiInfo ::Field & field )-> bool 50 {return fieldName == field .m_name ; }); 51if (index >=0 && structRttiInfo -> m_super ) 52 { 53index += _getFieldCount (structRttiInfo -> m_super ); 54 } 55 56return index ; 57} 58 59SlangResult JSONToNativeConverter ::_structToNative ( 60const ConstArrayView < JSONKeyValue >& pairs , 61const StructRttiInfo * structRttiInfo , 62void * out , 63Index & outFieldCount ) 64{ 65Index fieldCount = 0 ; 66 67if (structRttiInfo -> m_super ) 68 { 69SLANG_RETURN_ON_FAIL (_structToNative (pairs ,structRttiInfo -> m_super ,out ,fieldCount )); 70 } 71 72Byte * dst = (Byte * )out ; 73 74const Index count = structRttiInfo -> m_fieldCount ; 75 76for (Index i = 0 ;i < count ;++ i ) 77 { 78const auto & field = structRttiInfo -> m_fields [i ]; 79 80auto key = m_container -> findKey (UnownedStringSlice (field .m_name )); 81 82if (key == 0 ) 83 { 84if (field .m_flags & StructRttiInfo ::Flag ::Optional ) 85 { 86continue ; 87 } 88 89m_sink -> diagnose ( 90SourceLoc (), 91JSONDiagnostics ::fieldRequiredOnType , 92field .m_name , 93structRttiInfo -> m_name ); 94 95// Unable to find this key 96return SLANG_FAIL ; 97 } 98 99// If there are any of the pairs, that are not in the type.. it's an error 100const Index index = pairs .findFirstIndex ( 101 [key ](const JSONKeyValue & pair )-> bool {return pair .key == key ; }); 102if (index < 0 ) 103 { 104if (field .m_flags & StructRttiInfo ::Flag ::Optional ) 105 { 106continue ; 107 } 108 109m_sink -> diagnose ( 110SourceLoc (), 111JSONDiagnostics ::fieldRequiredOnType , 112field .m_name , 113structRttiInfo -> m_name ); 114 115// Unable to find this key 116return SLANG_FAIL ; 117 } 118 119auto & pair = pairs [index ]; 120 121// Copy the field over 122SLANG_RETURN_ON_FAIL (convert (pair .value ,field .m_type ,dst + field .m_offset )); 123 124// Field was handled 125++ fieldCount ; 126 } 127 128// Write off the amount of fields converted/handled. 129outFieldCount = fieldCount ; 130return SLANG_OK ; 131} 132 133SlangResult JSONToNativeConverter ::convert (const JSONValue & in ,const RttiInfo * rttiInfo ,void * out ) 134{ 135if (rttiInfo -> isIntegral ()) 136 { 137return RttiUtil ::setInt (m_container -> asInteger (in ),rttiInfo ,out ); 138 } 139else if (rttiInfo -> isFloat ()) 140 { 141return RttiUtil ::setFromDouble (m_container -> asFloat (in ),rttiInfo ,out ); 142 } 143 144switch (rttiInfo -> m_kind ) 145 { 146case RttiInfo ::Kind ::Bool : 147 { 148* (bool * )out = m_container -> asBool (in ); 149return SLANG_OK ; 150 } 151case RttiInfo ::Kind ::Struct : 152 { 153if (in .getKind ()!= JSONValue ::Kind ::Object ) 154 { 155return SLANG_FAIL ; 156 } 157 158auto pairs = m_container -> getObject (in ); 159const StructRttiInfo * structRttiInfo = static_cast < const StructRttiInfo *> (rttiInfo ); 160 161Index fieldCount = 0 ; 162SLANG_RETURN_ON_FAIL (_structToNative (pairs ,structRttiInfo ,out ,fieldCount )); 163 164if (fieldCount != pairs .getCount ()&& !structRttiInfo -> m_ignoreUnknownFieldsInJson ) 165 { 166// We want to find the fields not found in the type 167 168for (auto & pair :pairs ) 169 { 170UnownedStringSlice fieldName = m_container -> getStringFromKey (pair .key ); 171const Index index = 172_findFieldIndex (structRttiInfo ,UnownedStringSlice (fieldName )); 173 174if (index < 0 ) 175 { 176m_sink -> diagnose ( 177pair .keyLoc , 178JSONDiagnostics ::fieldNotDefinedOnType , 179fieldName , 180structRttiInfo -> m_name ); 181 } 182 } 183 184// If these are different then there are fields defined in the object that are *not* 185// defined in class definition 186return SLANG_FAIL ; 187 } 188 189return SLANG_OK ; 190 } 191case RttiInfo ::Kind ::Enum : 192 { 193return SLANG_E_NOT_IMPLEMENTED ; 194 } 195case RttiInfo ::Kind ::String : 196 { 197* (String * )out = m_container -> getTransientString (in ); 198return SLANG_OK ; 199 } 200case RttiInfo ::Kind ::UnownedStringSlice : 201 { 202// Problem -> if the slice is a lexeme, then when we decode with getString, it will lose 203// scope. So we do something a bit odd and place the decoding string 204 205* (UnownedStringSlice * )out = m_container -> getString (in ); 206return SLANG_OK ; 207 } 208case RttiInfo ::Kind ::Optional : 209 { 210if (in .getKind ()== JSONValue ::Kind ::Null ) 211 { 212return SLANG_OK ; 213 } 214typedef List < Byte > Type ; 215const OptionalRttiInfo * optionalRttiInfo = 216static_cast < const OptionalRttiInfo *> (rttiInfo ); 217auto hasValue = (uint8_t * )out ; 218* hasValue = 1 ; 219return convert ( 220in , 221optionalRttiInfo -> m_elementType , 222 (uint8_t * )out + optionalRttiInfo -> m_valueOffset ); 223 } 224case RttiInfo ::Kind ::List : 225 { 226if (in .getKind ()== JSONValue ::Kind ::Null ) 227return SLANG_OK ; 228if (in .getKind ()!= JSONValue ::Kind ::Array ) 229 { 230return SLANG_FAIL ; 231 } 232 233typedef List < Byte > Type ; 234Type & list = * (Type * )out ; 235 236auto arr = m_container -> getArray (in ); 237 238const Index count = arr .getCount (); 239 240const ListRttiInfo * listRttiInfo = static_cast < const ListRttiInfo *> (rttiInfo ); 241auto elementType = listRttiInfo -> m_elementType ; 242 243SLANG_RETURN_ON_FAIL ( 244RttiUtil ::setListCount (m_typeMap ,elementType ,out ,arr .getCount ())); 245 246// Okay, we need to copy over one by one 247Byte * dstEles = list .getBuffer (); 248for (Index i = 0 ;i < count ;++ i ,dstEles += elementType -> m_size ) 249 { 250SLANG_RETURN_ON_FAIL (convert (arr [i ],elementType ,dstEles )); 251 } 252 253return SLANG_OK ; 254 } 255case RttiInfo ::Kind ::FixedArray : 256 { 257if (in .getKind ()!= JSONValue ::Kind ::Array ) 258 { 259return SLANG_FAIL ; 260 } 261const FixedArrayRttiInfo * fixedArrayRttiInfo = 262static_cast < const FixedArrayRttiInfo *> (rttiInfo ); 263const auto elementType = fixedArrayRttiInfo -> m_elementType ; 264const Index elementCount = Index (fixedArrayRttiInfo -> m_elementCount ); 265const auto elementSize = elementType -> m_size ; 266 267auto srcArray = m_container -> getArray (in ); 268 269if (srcArray .getCount ()> elementCount ) 270 { 271m_sink -> diagnose ( 272in .loc , 273JSONDiagnostics ::tooManyElementsForArray , 274srcArray .getCount (), 275elementCount ); 276return SLANG_FAIL ; 277 } 278 279Byte * dstEles = (Byte * )out ; 280for (Index i = 0 ;i < elementCount ;++ i ,dstEles += elementSize ) 281 { 282SLANG_RETURN_ON_FAIL (convert (srcArray [i ],elementType ,dstEles )); 283 } 284 285return SLANG_OK ; 286 } 287case RttiInfo ::Kind ::Dictionary : 288 { 289// We can *only* serialize this into a straight JSON object iff the key is a string-like 290// type We could turn into (say) an array of keys and values 291break ; 292 } 293case RttiInfo ::Kind ::Other : 294 { 295if (rttiInfo == GetRttiInfo < JSONValue > ::get ()) 296 { 297// Do we need to copy into the container? 298// As it stands we have to assume src is stored in container. 299* (JSONValue * )out = in ; 300return SLANG_OK ; 301 } 302return SLANG_FAIL ; 303 } 304default : 305break ; 306 } 307return SLANG_FAIL ; 308} 309 310SlangResult JSONToNativeConverter ::convertArrayToStruct ( 311const JSONValue & value , 312const RttiInfo * rttiInfo , 313void * out ) 314{ 315// Check converting JSON array into a struct, as that's what this method supports 316if (!(rttiInfo -> m_kind == RttiInfo ::Kind ::Struct && value .getKind ()== JSONValue ::Kind ::Array )) 317 { 318// If they are the wrong types then just fail 319return SLANG_FAIL ; 320 } 321 322// Find the total amount of fields, and all the classes involved 323Index totalFieldCount = 0 ; 324 325ShortList < const StructRttiInfo * ,8 > infos ; 326for (const StructRttiInfo * cur = static_cast < const StructRttiInfo *> (rttiInfo );cur ; 327cur = cur -> m_super ) 328 { 329totalFieldCount += cur -> m_fieldCount ; 330infos .add (cur ); 331 } 332 333// Must have the same amount of fields 334auto array = m_container -> getArray (value ); 335if (array .getCount ()!= totalFieldCount ) 336 { 337return SLANG_FAIL ; 338 } 339 340Byte * dstBase = (Byte * )out ; 341 342// We work in the order from the base class to the final type 343Index argIndex = 0 ; 344for (Index i = infos .getCount ()- 1 ;i >=0 ;-- i ) 345 { 346auto info = infos [i ]; 347 348const Index fieldCount = info -> m_fieldCount ; 349for (Index j = 0 ;j < fieldCount ;++ j ) 350 { 351// Convert the field 352const auto & field = info -> m_fields [j ]; 353SLANG_RETURN_ON_FAIL ( 354convert (array [argIndex ++ ],field .m_type ,dstBase + field .m_offset )); 355 } 356 } 357 358return SLANG_OK ; 359} 360 361/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! NativeToJSONConverter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ 362 363SlangResult NativeToJSONConverter ::_structToJSON ( 364const StructRttiInfo * structRttiInfo , 365const void * src , 366List < JSONKeyValue >& outPairs ) 367{ 368// Do the super class first 369if (structRttiInfo -> m_super ) 370 { 371SLANG_RETURN_ON_FAIL (_structToJSON (structRttiInfo -> m_super ,src ,outPairs )); 372 } 373 374const Byte * base = (const Byte * )src ; 375const Index count = structRttiInfo -> m_fieldCount ; 376 377for (Index i = 0 ;i < count ;++ i ) 378 { 379const auto & field = structRttiInfo -> m_fields [i ]; 380 381if (field .m_flags & StructRttiInfo ::Flag ::Optional ) 382 { 383const RttiDefaultValue defaultValue = 384RttiDefaultValue (field .m_flags & uint8_t (RttiDefaultValue ::Mask )); 385if (RttiUtil ::isDefault (defaultValue ,field .m_type ,base + field .m_offset )) 386 { 387// If it's a default, we don't bother writing it 388continue ; 389 } 390 } 391 392JSONKeyValue pair ; 393pair .key = m_container -> getKey (UnownedStringSlice (field .m_name )); 394auto res = convert (field .m_type ,base + field .m_offset ,pair .value ); 395 396if (SLANG_FAILED (res )) 397 { 398m_sink -> diagnose ( 399SourceLoc (), 400JSONDiagnostics ::unableToConvertField , 401field .m_name , 402structRttiInfo -> m_name ); 403return res ; 404 } 405 406outPairs .add (pair ); 407 } 408 409return SLANG_OK ; 410} 411 412 413SlangResult NativeToJSONConverter ::convert (const RttiInfo * rttiInfo ,const void * in ,JSONValue & out ) 414{ 415if (rttiInfo -> isIntegral ()) 416 { 417out = JSONValue ::makeInt (RttiUtil ::getInt64 (rttiInfo ,in )); 418return SLANG_OK ; 419 } 420else if (rttiInfo -> isFloat ()) 421 { 422out = JSONValue ::makeFloat (RttiUtil ::asDouble (rttiInfo ,in )); 423return SLANG_OK ; 424 } 425 426switch (rttiInfo -> m_kind ) 427 { 428case RttiInfo ::Kind ::Invalid : 429return SLANG_FAIL ; 430case RttiInfo ::Kind ::Bool : 431 { 432out = JSONValue ::makeBool (RttiUtil ::asBool (rttiInfo ,in )); 433return SLANG_OK ; 434 } 435case RttiInfo ::Kind ::String : 436 { 437const String & str = * (const String * )in ; 438out = m_container -> createString (str .getUnownedSlice ()); 439return SLANG_OK ; 440 } 441case RttiInfo ::Kind ::UnownedStringSlice : 442 { 443const UnownedStringSlice & slice = * (const UnownedStringSlice * )in ; 444out = m_container -> createString (slice ); 445return SLANG_OK ; 446 } 447case RttiInfo ::Kind ::Struct : 448 { 449const StructRttiInfo * structRttiInfo = static_cast < const StructRttiInfo *> (rttiInfo ); 450 451List < JSONKeyValue > pairs ; 452SLANG_RETURN_ON_FAIL (_structToJSON (structRttiInfo ,in ,pairs )); 453out = m_container -> createObject (pairs .getBuffer (),pairs .getCount ()); 454return SLANG_OK ; 455 } 456case RttiInfo ::Kind ::Enum : 457 { 458return SLANG_E_NOT_IMPLEMENTED ; 459 } 460case RttiInfo ::Kind ::Optional : 461 { 462const OptionalRttiInfo * optionalRttiInfo = 463static_cast < const OptionalRttiInfo *> (rttiInfo ); 464auto hasValue = (const uint8_t * )in ; 465if (* hasValue ) 466 { 467return convert ( 468optionalRttiInfo -> m_elementType , 469 (const uint8_t * )in + optionalRttiInfo -> m_valueOffset , 470out ); 471 } 472else 473 { 474out = JSONValue ::makeNull (); 475return SLANG_OK ; 476 } 477 } 478case RttiInfo ::Kind ::List : 479 { 480const ListRttiInfo * listRttiInfo = static_cast < const ListRttiInfo *> (rttiInfo ); 481const auto elementRttiInfo = listRttiInfo -> m_elementType ; 482 483// The src probably *doesn't* contain bytes, but can cast like this because 484// we only need the count (which doesn't depend on <T>), and the backing buffer 485const List < Byte >& srcValuesList = * (const List < Byte >* )in ; 486 487const Index count = srcValuesList .getCount (); 488const Byte * srcValues = srcValuesList .getBuffer (); 489 490List < JSONValue > dstValues ; 491dstValues .setCount (count ); 492 493const size_t elementStride = elementRttiInfo -> m_size ; 494 495for (Index i = 0 ;i < count ;++ i ,srcValues += elementStride ) 496 { 497SLANG_RETURN_ON_FAIL (convert (elementRttiInfo ,srcValues ,dstValues [i ])); 498 } 499 500out = m_container -> createArray (dstValues .getBuffer (),count ); 501return SLANG_OK ; 502 } 503case RttiInfo ::Kind ::FixedArray : 504 { 505const FixedArrayRttiInfo * fixedArrayRttiInfo = 506static_cast < const FixedArrayRttiInfo *> (rttiInfo ); 507const auto elementType = fixedArrayRttiInfo -> m_elementType ; 508const auto elementCount = Index (fixedArrayRttiInfo -> m_elementCount ); 509const auto elementSize = elementType -> m_size ; 510 511List < JSONValue > dstValues ; 512dstValues .setCount (elementCount ); 513 514const Byte * src = (const Byte * )in ; 515for (Index i = 0 ;i < elementCount ;++ i ,src += elementSize ) 516 { 517SLANG_RETURN_ON_FAIL (convert (elementType ,src ,dstValues [i ])); 518 } 519 520out = m_container -> createArray (dstValues .getBuffer (),elementCount ); 521return SLANG_OK ; 522 } 523case RttiInfo ::Kind ::Dictionary : 524 { 525const DictionaryRttiInfo * listRttiInfo = 526static_cast < const DictionaryRttiInfo *> (rttiInfo ); 527const auto keyRttiInfo = listRttiInfo -> m_keyType ; 528const auto valueRttiInfo = listRttiInfo -> m_valueType ; 529 530SLANG_UNUSED (keyRttiInfo ); 531SLANG_UNUSED (valueRttiInfo ); 532 533// We can *only* serialize this into a straight JSON object iff the key is a string-like 534// type We could turn into (say) an array of keys and values 535 536break ; 537 } 538case RttiInfo ::Kind ::Other : 539 { 540if (rttiInfo == GetRttiInfo < JSONValue > ::get ()) 541 { 542// Do we need to copy into the container? 543// As it stands we have to assume src is stored in container. 544const JSONValue & src = * (const JSONValue * )in ; 545 546out = src ; 547return SLANG_OK ; 548 } 549break ; 550 } 551default : 552break ; 553 } 554 555return SLANG_E_NOT_IMPLEMENTED ; 556} 557 558SlangResult NativeToJSONConverter ::convertStructToArray ( 559const RttiInfo * rttiInfo , 560const void * in , 561JSONValue & out ) 562{ 563if (rttiInfo -> m_kind != RttiInfo ::Kind ::Struct ) 564 { 565// Must be a struct 566return SLANG_FAIL ; 567 } 568 569// Work out the total amount of fields, and all invloved struct types 570Index totalFieldsCount = 0 ; 571ShortList < const StructRttiInfo * ,8 > infos ; 572for (const StructRttiInfo * cur = static_cast < const StructRttiInfo *> (rttiInfo );cur ; 573cur = cur -> m_super ) 574 { 575totalFieldsCount += Index (cur -> m_fieldCount ); 576infos .add (cur ); 577 } 578 579// Convert the args/params 580List < JSONValue > argsArray ; 581argsArray .setCount (totalFieldsCount ); 582 583// NOTE! We do no special handling here around optional parameters. 584// All fields of the input args are output 585 { 586Index argsArrayIndex = 0 ; 587const Byte * argsBase = (const Byte * )in ; 588 589// Work in the order from the base class to the actual type 590for (Index i = infos .getCount ()- 1 ;i >=0 ;-- i ) 591 { 592auto structRttiInfo = infos [i ]; 593const Index fieldCount = Index (structRttiInfo -> m_fieldCount ); 594 595for (Index j = 0 ;j < fieldCount ;++ j ) 596 { 597const auto & field = structRttiInfo -> m_fields [j ]; 598// Convert the field 599SLANG_RETURN_ON_FAIL ( 600convert (field .m_type ,argsBase + field .m_offset ,argsArray [argsArrayIndex ++ ])); 601 } 602 } 603 } 604 605// Okay now we convert the List to the output, which will just be a JSON array. 606SLANG_RETURN_ON_FAIL (convert (& argsArray ,out )); 607 608return SLANG_OK ; 609} 610 611}// namespace Slang