|
libose
|
provides global definitions and types More...
#include <limits.h>#include <inttypes.h>Go to the source code of this file.
Data Structures | |
| struct | ose_bundle |
| struct | ose_timetag |
| Convenience struct for OSC/NTP timetags. More... | |
| struct | osevm_hooks |
Macros | |
| #define | OSE_CONF_DEBUG |
| #define | OSE_CONF_MEMPROFILE |
| Enable memory profiling. | |
| #define | OSE_CONF_WRAP_BUNDLE_PTR |
| Cause the ose_bundle type to be a pointer wrapped in a struct. | |
| #define | OSE_WRAP_BUNDLE_PTR |
| #define | OSE_MEMPROFILE |
| #define | OSE_HOT |
| Where a host wants the interpreter's hottest code placed. | |
| #define | ose_memmove memmove |
| Move bytes that may overlap: memmove, or on the ESP32 a faster way. | |
| #define | OSE_CONF_PROVIDE_TYPE_SYMBOL |
| #define | OSE_CONF_PROVIDE_TYPE_DOUBLE |
| #define | OSE_CONF_PROVIDE_TYPE_INT8 |
| #define | OSE_CONF_PROVIDE_TYPE_UINT8 |
| #define | OSE_CONF_PROVIDE_TYPE_INT16 |
| #define | OSE_CONF_PROVIDE_TYPE_UINT16 |
| #define | OSE_CONF_PROVIDE_TYPE_UINT32 |
| #define | OSE_CONF_PROVIDE_TYPE_INT64 |
| #define | OSE_CONF_PROVIDE_TYPE_UINT64 |
| #define | OSE_CONF_PROVIDE_TYPE_TIMETAG |
| #define | OSE_CONF_PROVIDE_TYPE_TRUE |
| #define | OSE_CONF_PROVIDE_TYPE_FALSE |
| #define | OSE_CONF_PROVIDE_TYPE_NIL |
| #define | OSE_CONF_PROVIDE_TYPE_INFINITUM |
Functions | |
Version | |
| const char * | ose_version_string (void) |
| const char * | ose_debug_string (void) |
| const char * | ose_date_compiled_string (void) |
Types | |
Check after every instruction that the VM's bundles are still valid OSC. The claim the design rests on is that a VM's state is a well formed OSC bundle at all times – that the running machine and the wire format are the same bytes. Nothing checked it. Define OSE_CONF_VALIDATE_VM_STEP and every context bundle is validated after every instruction, so an instruction that can produce malformed bytes says so at the point it does it, with the address that did it still to hand. Off by default and meant to stay that way: it validates five bundles per instruction, which is far too expensive to ship. Turn it on to run a test suite or a program under, the way you would a sanitizer. | |
| #define | ose_constbundle ose_bundle |
| #define | ose_makeConstBundle ose_makeBundle |
| #define | ose_getBundlePtr(bundle) (bundle).b |
| #define | OSE_INTPTR2 (sizeof(intptr_t) * 2) |
Define a size that is twice that of intptr_t. | |
| enum | bool { false = 0 , true } |
| typedef struct ose_bundle | ose_bundle |
| typedef void(* | ose_fn) (ose_bundle) |
| Function pointer for the basic OSE function that operates on a bundle. | |
| ose_bundle | ose_makeBundle (const char *const p) |
Extended Types | |
| #define | OSE_PROVIDE_TYPE_SYMBOL |
| #define | OSETT_SYMBOL_ 'S' |
| #define | OSETTSTR_SYMBOL_ "S" |
| #define | OSE_PROVIDE_TYPE_DOUBLE |
| #define | OSETT_DOUBLE_ 'd' |
| #define | OSETTSTR_DOUBLE_ "d" |
| #define | OSE_PROVIDE_TYPE_INT8 |
| #define | OSETT_INT8_ 'c' |
| #define | OSETTSTR_INT8_ "c" |
| #define | OSE_PROVIDE_TYPE_UINT8 |
| #define | OSETT_UINT8_ 'C' |
| #define | OSETTSTR_UINT8_ "C" |
| #define | OSE_PROVIDE_TYPE_INT16 |
| #define | OSETT_INT16_ 'u' |
| #define | OSETTSTR_INT16_ "u" |
| #define | OSE_PROVIDE_TYPE_UINT16 |
| #define | OSETT_UINT16_ 'U' |
| #define | OSETTSTR_UINT16_ "U" |
| #define | OSE_PROVIDE_TYPE_UINT32 |
| #define | OSETT_UINT32_ 'k' |
| #define | OSETTSTR_UINT32_ "k" |
| #define | OSE_PROVIDE_TYPE_INT64 |
| #define | OSETT_INT64_ 'h' |
| #define | OSETTSTR_INT64_ "h" |
| #define | OSE_PROVIDE_TYPE_UINT64 |
| #define | OSETT_UINT64_ 'H' |
| #define | OSETTSTR_UINT64_ "H" |
| #define | OSE_PROVIDE_TYPE_TIMETAG |
| #define | OSETT_TIMETAG_ 't' |
| #define | OSETTSTR_TIMETAG_ "t" |
| #define | OSE_PROVIDE_TYPE_TRUE |
| #define | OSETT_TRUE_ 'T' |
| #define | OSETTSTR_TRUE_ "T" |
| #define | OSE_PROVIDE_TYPE_FALSE |
| #define | OSETT_FALSE_ 'F' |
| #define | OSETTSTR_FALSE_ "F" |
| #define | OSE_PROVIDE_TYPE_NIL |
| #define | OSETT_NIL_ 'N' |
| #define | OSETTSTR_NIL_ "N" |
| #define | OSE_PROVIDE_TYPE_INFINITUM |
| #define | OSETT_INFINITUM_ 'I' |
| #define | OSETTSTR_INFINITUM_ "I" |
| static const char | OSETT_SYMBOL = 'S' |
| static const char *const | OSETTSTR_SYMBOL = "S" |
| static const char | OSETT_DOUBLE = 'd' |
| static const char *const | OSETTSTR_DOUBLE = "d" |
| static const char | OSETT_INT8 = 'c' |
| static const char *const | OSETTSTR_INT8 = "c" |
| static const char | OSETT_UINT8 = 'C' |
| static const char *const | OSETTSTR_UINT8 = "C" |
| static const char | OSETT_INT16 = 'u' |
| static const char *const | OSETTSTR_INT16 = "u" |
| static const char | OSETT_UINT16 = 'U' |
| static const char *const | OSETTSTR_UINT16 = "U" |
| static const char | OSETT_UINT32 = 'k' |
| static const char *const | OSETTSTR_UINT32 = "k" |
| static const char | OSETT_INT64 = 'h' |
| static const char *const | OSETTSTR_INT64 = "h" |
| static const char | OSETT_UINT64 = 'H' |
| static const char *const | OSETTSTR_UINT64 = "H" |
| static const char | OSETT_TIMETAG = 't' |
| static const char *const | OSETTSTR_TIMETAG = "t" |
| static const char | OSETT_TRUE = 'T' |
| static const char *const | OSETTSTR_TRUE = "T" |
| static const char *const | ose_str_true = "/,/T" |
| static const int32_t | ose_strlen_true = 4 |
| static const int32_t | ose_pstrlen_true = 8 |
| static const char | OSETT_FALSE = 'F' |
| static const char *const | OSETTSTR_FALSE = "F" |
| static const char *const | ose_str_false = "/,/F" |
| static const int32_t | ose_strlen_false = 4 |
| static const int32_t | ose_pstrlen_false = 8 |
| static const char | OSETT_NIL = 'N' |
| static const char *const | OSETTSTR_NIL = "N" |
| static const char *const | ose_str_nil = "/,/N" |
| static const int32_t | ose_strlen_nil = 4 |
| static const int32_t | ose_pstrlen_nil = 8 |
| static const char | OSETT_INFINITUM = 'I' |
| static const char *const | OSETTSTR_INFINITUM = "I" |
| static const char *const | ose_str_infinitum = "/,/I" |
| static const int32_t | ose_strlen_infinitum = 4 |
| static const int32_t | ose_pstrlen_infinitum = 8 |
Extension type | |
The one type libose defines that is not in OSC 1.0 and that is written into a typetag string. Deliberate, and the cost is known. OSC 1.0 says an application "should discard any message whose OSC Type Tag String contains any unrecognized OSC Type Tags", so a message carrying this one is dropped whole by a conformant peer rather than read with an argument missing. It is therefore for messages that stay inside a process, and OSE_ERR_UNTRUSTED_TYPE is what keeps them there. It earns that. A bound function has to be stored, not merely accepted: ose_registerType_hook keeps its five hooks this way and the VM pushes its own instructions as aligned pointers. Pointers were written as blobs before, where nothing could hold one and – the reason this exists – nothing could tell one from data arriving off a wire. The tag is what lets the input walk refuse it. | |
| #define | OSETT_ALIGNEDPTR_ 'p' |
| #define | OSETTSTR_ALIGNEDPTR_ "p" |
| #define | OSETT_WILDPTR_ 'P' |
| A bound function from another process, kept but not callable. | |
| #define | OSETTSTR_WILDPTR_ "P" |
| static const char | OSETT_ALIGNEDPTR = 'p' |
| static const char *const | OSETTSTR_ALIGNEDPTR = "p" |
| static const char | OSETT_WILDPTR = 'P' |
| static const char *const | OSETTSTR_WILDPTR = "P" |
Constants | |
| #define | OSE_BUNDLE_ID_ "#bundle\0" |
| #define | OSE_BUNDLE_ID_LEN_ 8 |
| #define | OSE_TIMETAG_NULL_ "\0\0\0\0\0\0\0\0" |
| #define | OSE_TIMETAG_LEN_ 8 |
| #define | OSE_EMPTY_TYPETAG_STRING_ ",\0\0\0" |
| #define | OSE_ADDRESS_ANONVAL_ "\0\0\0\0" |
| static const char *const | OSE_BUNDLE_ID = "#bundle\0" |
| static const int32_t | OSE_BUNDLE_ID_LEN = 8 |
| static const char *const | OSE_TIMETAG_NULL = "\0\0\0\0\0\0\0\0" |
| static const int32_t | OSE_TIMETAG_LEN = 8 |
| static const char *const | OSE_BUNDLE_HEADER |
| static const int32_t | OSE_BUNDLE_HEADER_LEN |
| static const char *const | OSE_EMPTY_TYPETAG_STRING |
| static const int32_t | OSE_EMPTY_TYPETAG_STRING_LEN = 1 |
| static const int32_t | OSE_EMPTY_TYPETAG_STRING_SIZE = 4 |
| static const char *const | OSE_ADDRESS_ANONVAL = "\0\0\0\0" |
| static const int32_t | OSE_ADDRESS_ANONVAL_LEN = 0 |
| static const int32_t | OSE_ADDRESS_ANONVAL_SIZE = 4 |
| static const char *const | OSE_ADDRESS_ANONVAL_EMPTY_TYPETAG_STRING |
| static const int32_t | OSE_ADDRESS_MIN_LEN = 0 |
| static const int32_t | OSE_ADDRESS_MIN_PLEN = 4 |
Type Extensibility Hooks | |
| #define | OSE_TYPE_ADDR_PREFIX "/" |
| #define | OSE_TYPE_ADDR_PPRINTPAYLOADITEM_SUFFIX "/PPPI" |
| #define | OSE_PPRINTPAYLOADITEM_ADDR_ OSE_TYPE_ADDR_PREFIX "%c" OSE_TYPE_ADDR_PPRINTPAYLOADITEM_SUFFIX |
| #define | OSE_PPRINTPAYLOADITEM_HOOK ose_pprintPayloadItem_hook |
| #define | OSE_TYPE_ADDR_GETPAYLOADITEMLENGTH_SUFFIX "/GPIL" |
| #define | OSE_GETPAYLOADITEMLENGTH_ADDR_ OSE_TYPE_ADDR_PREFIX "%c" OSE_TYPE_ADDR_GETPAYLOADITEMLENGTH_SUFFIX |
| #define | OSE_GETPAYLOADITEMLENGTH_HOOK ose_getPayloadItemLength_hook |
| #define | OSE_TYPE_ADDR_GETPAYLOADITEMSIZE_SUFFIX "/GPIS" |
| #define | OSE_GETPAYLOADITEMSIZE_ADDR_ OSE_TYPE_ADDR_PREFIX "%c" OSE_TYPE_ADDR_GETPAYLOADITEMSIZE_SUFFIX |
| #define | OSE_GETPAYLOADITEMSIZE_HOOK ose_getPayloadItemSize_hook |
| #define | OSE_TYPE_ADDR_FROMSTRING_SUFFIX "/FRST" |
| #define | OSE_FROMSTRING_ADDR_ OSE_TYPE_ADDR_PREFIX "%c" OSE_TYPE_ADDR_FROMSTRING_SUFFIX |
| #define | OSE_FROMSTRING_HOOK ose_fromString_hook |
| #define | OSE_REGISTERTYPE_HOOK ose_registerType_hook |
| typedef int32_t(* | ose_getPayloadItemLengthFn) (ose_bundle, char, int32_t, int32_t) |
| typedef int32_t(* | ose_getPayloadItemSizeFn) (ose_bundle, char, int32_t, int32_t) |
| typedef int32_t(* | ose_pprintPayloadItemFn) (ose_bundle, char, int32_t, int32_t) |
| typedef int32_t(* | ose_fromStringFn) (ose_bundle, char, int32_t, int32_t) |
| static const char *const | OSE_PPRINTPAYLOADITEM_ADDR |
| static const int32_t | OSE_PPRINTPAYLOADITEM_ADDR_LEN |
| static const char *const | OSE_GETPAYLOADITEMLENGTH_ADDR |
| static const int32_t | OSE_GETPAYLOADITEMLENGTH_ADDR_LEN |
| static const char *const | OSE_GETPAYLOADITEMSIZE_ADDR |
| static const int32_t | OSE_GETPAYLOADITEMSIZE_ADDR_LEN |
| static const char *const | OSE_FROMSTRING_ADDR |
| static const int32_t | OSE_FROMSTRING_ADDR_LEN |
provides global definitions and types
| #define OSE_HOT |
Where a host wants the interpreter's hottest code placed.
Marks the definitions the VM runs on every step: the step loop, apply, the stack operations and the bundle utilities they call. It is empty unless a host defines OSE_CONF_HOT, and a host that has a faster place for code than the default gives its attribute there.
The ESP32-S3 is why it exists. Code runs from external flash through a 16 KB instruction cache, and the VM's step path does not fit in it: measured on the board, a quarter of a tick was instruction-fetch stalls, and moving these files into internal RAM made the tick 1.28x faster.
| #define ose_memmove memmove |
Move bytes that may overlap: memmove, or on the ESP32 a faster way.
The ESP32-S3's memmove is its ROM's, and measured on the board it moved 1 KB in 9,300 cycles upward and 12,400 downward, where the ROM's memcpy took 679. Nearly every edit of a bundle is a memmove, so ose_memmove() moves overlapping bytes with memcpy instead, in pieces that do not overlap. The heart rate program's tick went from 13.1 to 10.1 ms. On every other host it is memmove.
| #define OSETT_WILDPTR_ 'P' |
A bound function from another process, kept but not callable.
Byte for byte a p – same payload, same size, same walks – and it differs in exactly three ways: the apply path will not call it, the input walk does accept it from outside, and it prints differently.
Why it exists. A p may not arrive from outside, and must not: accepting one would hand a remote peer the instruction pointer. But that refusal is whole-bundle, and it makes a legitimate thing impossible – a host running a single machine raises, and its handler ships the whole machine to a debugger somewhere else. That machine holds bound functions because every machine does, so the far end refuses all of it, and a single-machine host cannot be debugged remotely at all. The layer that saves a machine already declines to refuse one holding a bound function, deliberately – saving and restoring in one process is legitimate – so today such a machine saves fine and can be received nowhere.
A P is the honest form of that machine on someone else's wire: an address that is KNOWN to be meaningless here, which is strictly safer than a p, which claims not to be. A debugger can hold it, print it, walk it, and see where the bound functions were – and cannot call one.
Nothing retags automatically, and ose_validateBundle() in particular does not: it still refuses p from outside, unchanged. Disarming is a verb a program calls, so that admitting a foreign machine is a decision a receiver makes rather than a rule the door relaxes.
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |