libose
Loading...
Searching...
No Matches
Data Structures | Macros
ose.h File Reference

provides global definitions and types More...

#include <limits.h>
#include <inttypes.h>
Include dependency graph for ose.h:
This graph shows which files directly or indirectly include this file:

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)
 

OSC 1.0 Types

#define OSETT_ID_   ','
 
#define OSETTSTR_ID_   ","
 
#define OSETT_INT32_   'i'
 
#define OSETTSTR_INT32_   "i"
 
#define OSETT_FLOAT_   'f'
 
#define OSETTSTR_FLOAT_   "f"
 
#define OSETT_STRING_   's'
 
#define OSETTSTR_STRING_   "s"
 
#define OSETT_BLOB_   'b'
 
#define OSETTSTR_BLOB_   "b"
 
static const char OSETT_ID = ','
 
static const char *const OSETTSTR_ID = ","
 
static const char OSETT_INT32 = 'i'
 
static const char *const OSETTSTR_INT32 = "i"
 
static const char OSETT_FLOAT = 'f'
 
static const char *const OSETTSTR_FLOAT = "f"
 
static const char OSETT_STRING = 's'
 
static const char *const OSETTSTR_STRING = "s"
 
static const char OSETT_BLOB = 'b'
 
static const char *const OSETTSTR_BLOB = "b"
 

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"
 

Internal types

Types used in the API to say what something is, rather than to describe an argument.

This group was called "Pseudo-types" and described as types "used in the API, but never actually written into a typetag string". Corrected after an audit measured what each one does.

OSETT_NOTYPETAG and OSETT_MESSAGE keep that rule exactly. ose_getBundleElemType answers with them and nothing writes either one.

OSETT_BUNDLE keeps it as far as libose is concerned: as an element discriminator it never reaches a typetag string, and libose never puts a bundle inside a message. It is listed here rather than above because a client may do so, and libose sizes and validates such an item – from outside as well as in – so that a message holding one can be sent at all. Where that message must also be readable by a conformant peer, a blob carries the same bytes in the same layout and differs only in this byte.

#define OSETT_NOTYPETAG_   0
 
#define OSETTSTR_NOTYPETAG_   "0"
 
#define OSETT_BUNDLE_   '|'
 
#define OSETTSTR_BUNDLE_   "|"
 
#define OSETT_MESSAGE_   '-'
 
#define OSETTSTR_MESSAGE_   "-"
 
static const char OSETT_NOTYPETAG = 0
 
static const char *const OSETTSTR_NOTYPETAG = "0"
 
static const char OSETT_BUNDLE = '|'
 
static const char *const OSETTSTR_BUNDLE = "|"
 
static const char OSETT_MESSAGE = '-'
 
static const char *const OSETTSTR_MESSAGE = "-"
 

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
 

Detailed Description

provides global definitions and types

Macro Definition Documentation

◆ OSE_HOT

#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.

◆ ose_memmove

#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.

◆ OSETT_WILDPTR_

#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.

Variable Documentation

◆ OSE_ADDRESS_ANONVAL_EMPTY_TYPETAG_STRING

const char* const OSE_ADDRESS_ANONVAL_EMPTY_TYPETAG_STRING
static
Initial value:
=
"\0\0\0\0" ",\0\0\0"

◆ OSE_BUNDLE_HEADER

const char* const OSE_BUNDLE_HEADER
static
Initial value:
=
"#bundle\0" "\0\0\0\0\0\0\0\0"

◆ OSE_BUNDLE_HEADER_LEN

const int32_t OSE_BUNDLE_HEADER_LEN
static
Initial value:
=
( 8 + 8 )

◆ OSE_EMPTY_TYPETAG_STRING

const char* const OSE_EMPTY_TYPETAG_STRING
static
Initial value:
=
",\0\0\0"

◆ OSE_FROMSTRING_ADDR

const char* const OSE_FROMSTRING_ADDR
static
Initial value:
=
"/" "%c" "/FRST"

◆ OSE_FROMSTRING_ADDR_LEN

const int32_t OSE_FROMSTRING_ADDR_LEN
static
Initial value:
=
sizeof( "/" "%c" "/FRST" )

◆ OSE_GETPAYLOADITEMLENGTH_ADDR

const char* const OSE_GETPAYLOADITEMLENGTH_ADDR
static
Initial value:
=
"/" "%c" "/GPIL"

◆ OSE_GETPAYLOADITEMLENGTH_ADDR_LEN

const int32_t OSE_GETPAYLOADITEMLENGTH_ADDR_LEN
static
Initial value:
=
sizeof( "/" "%c" "/GPIL" )

◆ OSE_GETPAYLOADITEMSIZE_ADDR

const char* const OSE_GETPAYLOADITEMSIZE_ADDR
static
Initial value:
=
"/" "%c" "/GPIS"

◆ OSE_GETPAYLOADITEMSIZE_ADDR_LEN

const int32_t OSE_GETPAYLOADITEMSIZE_ADDR_LEN
static
Initial value:
=
sizeof( "/" "%c" "/GPIS" )

◆ OSE_PPRINTPAYLOADITEM_ADDR

const char* const OSE_PPRINTPAYLOADITEM_ADDR
static
Initial value:
=
"/" "%c" "/PPPI"

◆ OSE_PPRINTPAYLOADITEM_ADDR_LEN

const int32_t OSE_PPRINTPAYLOADITEM_ADDR_LEN
static
Initial value:
=
sizeof( "/" "%c" "/PPPI" )