TN-16 - OSC C-Language Parser/Constructor This note describes a C language OSC Parser/Constructor library. The library is designed for use in processes where the address space is fixed and dispatch is argument based. [1] The design requirements are that the library should: * be thread safe and re-entrant * not allocate any memory or call any other non-RT safe procedure * handle arbitrary length and variable length messages * provide all reasonable type coercion [2] * be efficient There are three primary public interfaces, the type 'osc_data_t' and the procedures 'osc_parse_message' and 'osc_construct_message'. There are two secondary public interfaces, the procedures 'osc_message_dsc' and 'osc_match_dsc'. 'osc_data_t' is a union of all valid OSC argument types. The field names are the same as the type characters. typedef union { int32_t i ; int64_t h ; uint64_t t ; float32_t f ; float64_t d ; const char *s ; const char *S ; uint32_t c ; uint32_t m ; uint32_t r ; struct { const char *data ; int32_t size ; } b ; } osc_data_t ; 'osc_parse_message' determines if an OSC message matches an address and a type descriptor string. If the message matches it parses the arguments into a provided data array in host order type coherent form. int32_t osc_parse_message ( const char *addr , const char *dsc , const char *packet , int32_t packet_sz , osc_data_t *data ) ; 'addr' is an address string. For OSC compliance the address should start with a forward slash. 'dsc' is a type string describing the arguments that the message must match. 'packet' is the OSC message, the message is 'packet_sz' bytes long. 'data' must be allocated to at least as many places as there are type tags at 'dsc'. If 'packet' matches 'addr' and 'dsc' the parser returns the offset to the end of the arguments as described in 'dsc' at 'packet', else the parser returns zero. 'packet' matches 'addr' iff the address strings are equal. 'packet' matches 'dsc' iff the packet contains at least as many arguments as given at 'dsc' and all of the encoded argument types can be coerced to the requested types. The parser implements type coercion between all numerical types, and between all string types. On a successful match the parser writes host order values for the types given at 'dsc' to 'data'. Any memory locations stored at 'data', for 's', 'S' and 'b' argument types, are of the read-only storage at 'packet'. Handling variable argument messages is only indirectly addressed by this design, importantly it is not precluded. The receiver can read the type descriptor of the incoming packet using 'osc_message_dsc'. The receiver can then evaluate this type descriptor against a dynamically determined required type descriptor using 'osc_match_dsc', which returns non-zero iff the type descriptors can be coerced one into the other. If the incoming packet is conforming the receiver uses the dynamically determined type descriptor to parse the incoming message. 'osc_construct_message' encodes an OSC message. int32_t osc_construct_message ( const char *addr , const char *dsc , const osc_data_t *data , char *packet , int32_t packet_sz ) 'osc_construct_message' encodes 'addr', 'dsc' and the host order arguments at 'data' as an OSC message writing to 'packet', which has 'packet_sz' places. The return value is the number of bytes the encoding uses, or zero if the encoding failed. Aside from corrupt 'data' the encoding should only fail because 'packet_sz' is inadequate to store the message. This library is demonstrated in jack.clock, see . This library requires the byte-order library documented in TN-15. [1] This is the OSC subset advocated for by James McCartney and implemented in SuperCollider. This subset of OSC is in essence a mechanism for byte encoding a typed vector with the requirement that the first element be a string. This subset does not combine the operator and the subject in the OSC address, the OSC address is the operator only and is matched using string equality. [2] OSC parsers should be lenient about incoming arguments types. OSC parsers in languages where names refer to entities of fixed type should not require a one to one relation between the type of the name that is to be bound to an argument of an OSC message and the type of the argument of the message. It is, for instance, reasonable for a process to encode all OSC numerical data as double precision floating point. Receivers should advertise that, for instance, a particular message requires an unsigned integer argument as an index to a frame buffer, but should accept that value in any numerical encoding.