Skip to content

Instantly share code, notes, and snippets.

@rsms
Created April 22, 2012 08:20
Show Gist options
  • Save rsms/2462695 to your computer and use it in GitHub Desktop.
Save rsms/2462695 to your computer and use it in GitHub Desktop.
I/O Patch implementation in C
//
// I/O Patch implementation in C
//
// A patch has strongly defined inputs and outputs, which can be connected to other
// patches' outputs and inputs to form program flow. A patch implements user code
// which reads zero or more immutable input values and mutates zero or more output
// values. This user code is implemented as conceptually pure functions (although
// the user is able to break pureness).
//
// Build and run:
// cc -o iopatch iopatch.c && ./iopatch
//
//
// Copyright (c) 2012 Rasmus Andersson <http://rsms.me/>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <memory.h>
#include <assert.h>
#include <unistd.h>
// -------------------------------------------------------------------------------
// Library
typedef void(*PatchFunction)(void*);
struct Patch;
typedef struct Connection {
struct Patch *inputState;
void *inputPtr;
} Connection;
typedef struct Connections {
size_t capacity;
size_t count;
Connection **connections;
} Connections;
typedef struct Patch {
PatchFunction func;
Connections *connections;
} Patch;
void _Connect(Patch *outputState, void *outputPtr,
Patch *inputState, void *inputPtr ) {
printf("Connect: %p:%zx -> %p:%zx\n",
outputState, ((size_t)outputPtr)-((size_t)outputState),
inputState, ((size_t)inputPtr)-((size_t)inputState));
Connection *connection = (Connection*)malloc(sizeof(Connection));
connection->inputState = inputState;
connection->inputPtr = inputPtr;
Connections *connections = outputState->connections;
if (connections == NULL) {
outputState->connections = connections = (Connections*)malloc(sizeof(Connections));
connections->capacity = 1;
connections->count = 1;
connections->connections = (Connection**)malloc(sizeof(void*)*connections->capacity);
connections->connections[0] = connection;
} else {
if (connections->capacity == connections->count) {
connections->capacity *= 2;
connections->connections = (Connection**)realloc(connections->connections,
sizeof(void*)*connections->capacity);
}
connections->connections[connections->count++] = connection;
}
}
// void Connect(Patch*, Symbol, Symbol, Patch*)
#define Connect(outPatch, outName, inName, inPatch) \
_Connect((Patch*)(outPatch), (void*)&((outPatch)->output__##outName), \
(Patch*)(inPatch), (void*)&((inPatch)->input__##inName) )
void _TriggerOutput(Patch *outputState, void *outputPtr, size_t outputSize) {
if (!outputState->connections) {
printf("TriggerOutput: Dead end -- no connections on output %p:%zx).\n",
outputState, ((size_t)outputPtr)-((size_t)outputState));
return;
}
Connections *connections = outputState->connections;
size_t i = 0, count = connections->count;
for (; i < count; ++i) {
//printf("TriggerOutput: Notify %zu\n", i);
Connection *receiver = connections->connections[i];
// assign input value
memcpy(receiver->inputPtr, outputPtr, outputSize);
// run receivers main function
assert(receiver->inputState != NULL);
void *x = (void*)receiver->inputState;
x = (void*)receiver->inputState->func;
receiver->inputState->func(receiver->inputState);
}
}
// void TriggerOutput(Patch*, Symbol)
#define TriggerOutput(outPatch, outName) \
_TriggerOutput((Patch*)(outPatch), (void*)&(outPatch)->output__##outName, \
sizeof((outPatch)->output__##outName));
Patch *_Create(size_t size, PatchFunction initFunc, PatchFunction runFunc) {
Patch *patch = (Patch*)calloc(1,size);
patch->func = runFunc;
if (initFunc) initFunc(patch);
return patch;
}
// Type* Create(Type)
#define Create(Type) (Type*)_Create(sizeof(Type), (PatchFunction)&Type##__init, \
(PatchFunction)&Type##__run)
// -------------------------------------------------------------------------------
// Patches
//
// Okay, here goes the basics:
//
// - A patch contains instance data (the struct) and a main function
// - Storage for any value is owned by an output of a patch instance
// - Inputs only reference data owned by some other output
// - Patch main functions are called when any input of a patch changed
// - The patch main function is responsible for calling TriggerOutput if
// it alters any output value.
// - TriggerOutput causes any connected patches to have their main
// functions executed (since effectively their input values changed).
//
// A patch that outputs received text to stdout and adds a trailing linebreak
typedef struct ConsoleOutput { Patch _;
char *input__text;
} ConsoleOutput;
void ConsoleOutput__init(ConsoleOutput *self) {};
void ConsoleOutput__run(ConsoleOutput *self) {
printf("ConsoleOutput@%p: ENTER\n", self);
if (isatty(1)) {
printf("\e[36;1m%s\e[0m\n", self->input__text);
} else {
printf("%s\n", self->input__text);
}
}
// A patch that converts a number to text
typedef struct NumberToText { Patch _;
double input__number;
char *output__text;
} NumberToText;
void NumberToText__init(NumberToText *self) {}
void NumberToText__run(NumberToText *self) {
printf("NumberToText@%p: ENTER\n", self);
// Allocate storage on first use
const size_t bufsize = 64;
if (self->output__text == NULL)
self->output__text = (char*)malloc(bufsize);
snprintf(self->output__text, bufsize, "%f", self->input__number);
printf("NumberToText: SET output String text = '%s'\n", self->output__text);
TriggerOutput(self, text);
}
// A patch which outputs the result from multiplying two input numbers
typedef struct Multiply { Patch _;
double input__value1;
double input__value2;
double output__result;
} Multiply;
void Multiply__init(Multiply *self) {}
void Multiply__run(Multiply *self) {
printf("Multiply@%p: ENTER\n", self);
self->output__result = self->input__value1 * self->input__value2;
printf("Multiply: SET output Number result = %f\n", self->output__result);
TriggerOutput(self, result);
}
// Compositing with subpatches
typedef struct CompositeExample { Patch _;
double input__number;
double output__number;
Multiply *multiply1;
Multiply *multiply2;
Multiply *multiply3;
} CompositeExample;
void CompositeExample__init(CompositeExample *self) {
// Create three subpatches
self->multiply1 = Create(Multiply);
self->multiply2 = Create(Multiply);
self->multiply3 = Create(Multiply);
// Connect the output from multiply1 to the two inputs of multiply2
Connect(self->multiply1,result, value1,self->multiply2);
Connect(self->multiply1,result, value2,self->multiply2);
// Connect the output from multiply2 to the two inputs of multiply3
Connect(self->multiply2,result, value1,self->multiply3);
Connect(self->multiply2,result, value2,self->multiply3);
}
void CompositeExample__run(CompositeExample *self) {
printf("CompositeExample@%p: ENTER\n", self);
// [hack] Assign input values and run the Multiply patch's function
self->multiply1->input__value1 = self->input__number;
self->multiply1->input__value2 = self->input__number;
Multiply__run(self->multiply1); // executes our subpatch pipe
// Copy the output value of multiply3 to our output number
self->output__number = self->multiply3->output__result;
printf("CompositeExample: SET output Number number = %f\n", self->output__number);
TriggerOutput(self, number);
}
// -------------------------------------------------------------------------------
// Driver
int main(int argc, char **argv) {
// Create patch instances
Multiply *multiply = Create(Multiply);
NumberToText *numberToText = Create(NumberToText);
NumberToText *numberToText2 = Create(NumberToText);
ConsoleOutput *consoleOutput = Create(ConsoleOutput);
CompositeExample *composite = Create(CompositeExample);
// Connect patches
Connect(multiply,result, /* --> */ number,numberToText);
Connect(multiply,result, /* --> */ number,numberToText2);
Connect(numberToText,text, /* --> */ text,consoleOutput);
Connect(multiply,result, /* --> */ number,composite);
Connect(composite,number, /* --> */ number,numberToText);
// Set some initial values on the root patch and run its function
multiply->input__value1 = 4.0;
multiply->input__value2 = 6.2;
Multiply__run(multiply);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment