implementing virtual machines in ruby & c
TRANSCRIPT
-
implementing virtual machines
in Ruby & C
Eleanor McHugh
@feyeleanor
http://github.com/feyeleanor
http://github.com/feyeleanor
-
I made my own machine Yes, we're building steam I hate the same routine
Building Steam, Abney Park
-
software is where machine meets thought
-
and as programmers we think a lot about thinking
-
whilst thinking very little about machines
-
we style ourselves philosophers & wizards
-
when machines need us to be engineers & analysts
-
so this is a talk about machine philosophy
-
framed in a language we humans can understand
-
so let's learn to love our Turing machines
-
by building other Turing machines' in their image
-
system virtualisation
-
hardware emulation
-
program execution
-
timely
stateful
conversational
discrete
-
despatch loops
fetch instruction
decode
execute
-
c: switch
bytes or tokens
portable
#include #include #include "stack.h"
#define READ_OPCODE *PC++
typedef enum {PUSH = 0,ADD,PRINT,EXIT
} opcodes;
int program [] = {(int)PUSH, 13,(int)PUSH, 28,(int)ADD,PRINT,EXIT,
};
STACK *S;
void interpret(int *PC) {int l, r;while (1) {
switch(READ_OPCODE) {case PUSH:
S = push(S, READ_OPCODE);break;
case ADD:S = pop(S, &l);S = pop(S, &r);S = push(S, l + r);break;
case PRINT:printf(%d + %d = %d\n, l, r, S->data);break;
case EXIT:return;
}}
}
int main() {interpret(program);
}
-
ruby: switch
symbols
class VMdef initialize(*program)@program = program@s = []@pc = 0
end
def interpretloop do
case read_programwhen :[email protected](read_program)
when :add@s[1] += @s[0]@s = @s.drop(1)
when :printputs "#{@s[0]}"
when :exitreturn
elseputs "#{op.class}"
endend
end
private def read_programr = @program[@pc]
@pc += 1r
endend
vm = VM.new(:push, 13,:push, 28,:add,:print,:exit,
)vm.interpret
-
c: direct call
pointer to function
multi-byte
portable
#include #include #include "stack.h"
#define READ_OPCODE *PC++typedef void (*opcode)();
STACK *S;opcode *PC;
void op_add_and_print() {int l, r;S = pop(S, &l);S = pop(S, &r);S = push(S, l + r);printf("%d + %d = %d\n", l, r, S->data);
}
void op_exit() {exit(0);
}
opcode program [] = {op_push, (opcode)(long)13,op_push, (opcode)(long)28,op_add_and_print,op_exit
};
int main() {PC = program;while (1) {
(READ_OPCODE)();}
}
-
ruby: direct call
method names
invoke via send
class VMdef initialize *program
@s = []@pc = 0@program = program.collect do |v|
self.respond_to?(v) ? self.method(v) : vend
end
def interpretcatch :program_complete do
loop doread_program.call
endend
end
def [email protected](read_program)
end
def exitthrow :program_complete
end
def add_and_print@s[1] += @s[0]@s = @s.drop(1)puts "#{@s[0]}"
end
private def read_programr = @program[@pc]@pc += 1r
end
def dangerous_methodraise "!!! I'M NOT A VALID OP_CODE !!!"
endend
VM.new(:push, 13,:push, 28,:dangerous_method,:add_and_print,:exit
).interpret
-
ruby: direct call
jit compilation
sandboxing
class VMBLACKLIST = [:load, :compile, :interpret] + Object.methods
def initialize *program@s = []load(program)
end
def load program@program = compile(program)self
end
def compile programprogram.collect do |v|
casewhen v.is_a?(Method)
raise "method injection of #{v.inspect} is not supported" if BLACKLIST.include?(v.name)raise "unknown method #{v.inspect}" unless methods.include?(v.name)v = v.unbindv.bind(self)
when methods.include?(v)raise "direct execution of #{v} is forbidden" if BLACKLIST.include?(v)self.method(v)
elsev
endend
end
-
ruby: direct call
jit compilation
sandboxing
def interpretcatch :program_complete do
@pc = 0loop do
read_program.callend
endend
end
beginVM.new(:dangerous_method)
rescue Exception => eputs "program compilation failed: #{e}"
end
vm = VM.newp = vm.compile([
:push, 13,:push, 28,:add_and_print,:exit
])
beginVM.new(*p)
rescue Exception => eputs "program compilation failed: #{e}"
end
vm.load(p).interpretVM.new(*p).interpret
-
c: indirect thread
local jumps
gcc/clang specific
indirect loading
#include #include #include "stack.h"
typedef enum {PUSH = 0, ADD, PRINT, EXIT
} opcodes;
void interpret(int *program) {static void *opcodes [] = {
&&op_push,&&op_add,&&op_print,&&op_exit
};
int l, r;STACK *S;int *PC = program;goto *opcodes[*PC++];
op_push:S = push(S, *PC++);goto *opcodes[*PC++];
op_add:S = pop(S, &l);S = pop(S, &r);S = push(S, l + r);goto *opcodes[*PC++];
op_print:printf("%d + %d = %d\n", l, r, S->data);goto *opcodes[*PC++];
op_exit:return;
}
int main() {int program [] = {
PUSH, 13,PUSH, 28,ADD,PRINT,EXIT
};interpret(program);
}
-
c: direct thread
jit local jumps
gcc/clang specific
direct loading
void interpret(int *PC, int words) {static void *dispatch_table[] = {
&&op_push,&&op_add,&&op_print,&&op_exit
};
STACK *S;int l, r;void **program = compile(PC, words, dispatch_table);if (program == NULL)
exit(1);goto **program++;
op_push:S = push(S, (int)(long)*program++);goto **program++;
op_add:S = pop(S, &l);S = pop(S, &r);S = push(S, l + r);goto **program++;
op_print:printf("%d + %d = %d\n", l, r, S->data);goto **program++;
op_exit:return;
}
int main() {int program[] = {
PUSH, 13,PUSH, 28,ADD,PRINT,EXIT
};interpret(program, 7);
}
-
c: direct thread
#define INTERPRETER(body, ...) \DISPATCHER(__VA_ARGS__); \void **p = compile(PC, d); \if (p == NULL) \
exit(1); \EXECUTE_OPCODE \body
#define DISPATCHER(...) \static void *d[] = { __VA_ARGS__ }
#define READ_OPCODE \*p++
#define EXECUTE_OPCODE \goto *READ_OPCODE;
#define PRIMITIVE(name, body) \name: body; \EXECUTE_OPCODE
void interpret(int *PC) {STACK *S;int l, r;INTERPRETER(
PRIMITIVE(push,S = push(S, (int)(long)READ_OPCODE)
)PRIMITIVE(add,
S = pop(S, &l);S = pop(S, &r);S = push(S, l + r)
)PRIMITIVE(print,
printf("%d + %d = %d\n", l, r, S->data))PRIMITIVE(exit,
return),&&push, &&add, &&print, &&exit)
}
-
c: direct thread
jit local jumps
gcc/clang specific
direct loading
void **compile(int *PC, int words, void *dispatch_table[]) {static void *compiler [] = {
&&comp_push,&&comp_add,&&comp_print,&&comp_exit
};
if (words < 1)return NULL;
void **program = malloc(sizeof(void *) * words);void **cp = program;goto *compiler[*PC++];
comp_push:*cp++ = dispatch_table[PUSH];*cp++ = (void *)(long)*PC++;words -= 2;if (words == 0)
return program;goto *compiler[*PC++];
comp_add:*cp++ = dispatch_table[ADD];words--;if (words == 0)
return program;goto *compiler[*PC++];
comp_print:*cp++ = dispatch_table[PRINT];words--;if (words == 0)
return program;goto *compiler[*PC++];
comp_exit:*cp++ = dispatch_table[EXIT];words--;if (words == 0)
return program;goto *compiler[*PC++];
}
-
ruby: direct thread
meta-magic
bind methods
stack explosion
probably not a good idea
-
registers
operands
local caching
-
vm harness
dispatch
program
program counter
class VMBL = [:load, :compile, :interpret] + Object.methods
def initialize *programload(program)
end
def load program@program = compile(program)self
end
def interpretcatch :program_complete do
@pc = 0loop do
read_program.callend
endend
def read_programr = @program[@pc]@pc += 1r
end
def compile programprogram.collect do |v|
casewhen v.is_a?(Method)
if BL.include?(v.name)raise "forbidden method: #{v.name}"
endunless methods.include?(v.name)
raise "unknown method: #{v.name}"endv = v.unbindv.bind(self)
when methods.include?(v)if BL.include?(v)
raise "forbidden method: #{v}"endself.method(v)
elsev
endend
endend
-
stack machine
zero operands
class Adder < VMdef interpret
@s = []super
end
def print_stateputs "#{@pc}: @s => #{@s}"
end
def [email protected](read_program)read_program.call
end
def add@s[1] += @s[0]@s = @s.drop(1)read_program.call
end
def exitthrow :program_complete
end
def jump_if_not_zeroif @s[0] == 0
@pc += 1else
@pc = @program[@pc]endread_program.call
endend
Adder.new(:push, 13,:push, -1,:add,:print_state,:read_program,:print_state,:jump_if_not_zero, 2,:exit
).interpret
-
accumulator machine
single register
single operand
class Adder < VMdef interpret
@s = []@a = 0super
end
def print_stateputs "#{@pc}: @a = #{@a}, @s => #{@s}"
end
def clear@a = 0read_program.call
end
def [email protected](read_program)read_program.call
end
def [email protected](@accum)read_program.call
end
def add@a += @s.popread_program.call
end
def jump_if_not_zeroif @a == 0
@pc += 1else
@pc = @program[@pc]endread_program.call
end
def exitthrow :program_complete
endend
Adder.new(:clear,:push_value, 13,:print_state,:add,:print_state,:push_value, -1,:print_state,:add,:print_state,:read_program,:print_state,:jump_if_not_zero, 6,:exit
).interpret
-
register machine
multi-register
multi-operand
class Adder < VMdef interpret
@r = Array.new(2, 0)super
end
def load_value@r[read_program] = read_programread_program.call
end
def add@r[read_program] += @r[read_program]read_program.call
end
def jump_if_not_zeroif @r[read_program] == 0
@pc += 1else
@pc = @program[@pc]endread_program.call
end
def exitthrow :program_complete
end
def print_stateputs "#{@pc}: @r => #{@r}"
endend
Adder.new(:load_value, 0, 13,:load_value, 1, -1,:print_state,:add, 0, 1,:print_state,:jump_if_not_zero, 0, 7,:read_program,:print_state,:exit
).interpret
-
vector machine
matrix machine
hypercube
graph processor
any datatype can be a register
-
memory model
instructions
computation
state
-
memory model
opcodes
stack
heap
-
heaps
word-aligned
contiguous
byte-addressable
-
ruby: array heap
core class
s = []s.push(1)s.push(3)
puts "depth = #{s.length}"l = s.popr = s.popputs "#{l} + #{r} = #{l + r}"puts "depth = #{s.length}"
-
ruby: c heap
require "fiddle"
class Fiddle::PointerNIL = Pointer.new(0)SIZE = Fixnum::SIZE
PACKING_PATTERN = case SIZEwhen 2 then "S"when 4 then "L"when 8 then "Q"end + "!"
def write(value)str = Fiddle::format(value)pad = Fiddle::padding(str)l = pad + str.lengthraise BufferOverflow.new(self, l) if l > sizeself[0, l] = str + 0.chr * padself + l
end
def to_bin[self].pack(PACKING_PATTERN)
endend
-
Home (./index.html) Classes(./index.html#classes) Methods(./index.html#methods)
In Files
fiddle/closure.c
fiddle/fiddle.c
fiddle/lib/fiddle.rb
fiddle/lib/fiddle/closure.rb
fiddle/lib/fiddle/cparser.rb
fiddle/lib/fiddle/function.rb
fiddle/lib/fiddle/import.rb
fiddle/lib/fiddle/pack.rb
fiddle/lib/fiddle/struct.rb
fiddle/lib/fiddle/types.rb
fiddle/lib/fiddle/value.rb
Namespace
MODULE Fiddle::BasicTypes (Fiddle/BasicTypes.html)MODULE Fiddle::CParser (Fiddle/CParser.html)MODULE Fiddle::CStructBuilder (Fiddle/CStructBuilder.html)MODULE Fiddle::Importer (Fiddle/Importer.html)MODULE Fiddle::Win32Types (Fiddle/Win32Types.html)CLASS Fiddle::CStruct (Fiddle/CStruct.html)CLASS Fiddle::CStructEntity (Fiddle/CStructEntity.html)CLASS Fiddle::CUnion (Fiddle/CUnion.html)CLASS Fiddle::CUnionEntity (Fiddle/CUnionEntity.html)CLASS Fiddle::Closure (Fiddle/Closure.html)CLASS Fiddle::CompositeHandler (Fiddle/CompositeHandler.html)CLASS Fiddle::DLError (Fiddle/DLError.html)CLASS Fiddle::Function (Fiddle/Function.html)CLASS Fiddle::Handle (Fiddle/Handle.html)CLASS Fiddle::Pointer (Fiddle/Pointer.html)
Methods
::dlopen (#method-c-dlopen)::dlunwrap (#method-c-dlunwrap)::dlwrap (#method-c-dlwrap)::free (#method-c-free)::last_error (#method-c-last_error)::last_error= (#method-c-last_error-3D)::malloc (#method-c-malloc)::realloc (#method-c-realloc)::win32_last_error (#method-c-win32_last_error)::win32_last_error= (#method-c-win32_last_error-3D)
Fiddle (./Fiddle.html)Fiddle::BasicTypes (./Fiddle/BasicTypes.html)Fiddle::CParser (./Fiddle/CParser.html)Fiddle::CStruct (./Fiddle/CStruct.html)Fiddle::CStructBuilder (./Fiddle/CStructBuilder.html)Fiddle::CStructEntity (./Fiddle/CStructEntity.html)Fiddle::CUnion (./Fiddle/CUnion.html)Fiddle::CUnionEntity (./Fiddle/CUnionEntity.html)Fiddle::Closure (./Fiddle/Closure.html)Fiddle::Closure::BlockCaller (./Fiddle/Closure/BlockCaller.html)Fiddle::CompositeHandler (./Fiddle/CompositeHandler.html)Fiddle::DLError (./Fiddle/DLError.html)Fiddle::Function (./Fiddle/Function.html)Fiddle::Handle (./Fiddle/Handle.html)Fiddle::Importer (./Fiddle/Importer.html)Fiddle::Pointer (./Fiddle/Pointer.html)Fiddle::Win32Types (./Fiddle/Win32Types.html)
(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSIP27JCWA4YKQWCAADEKQICKBI4Z3JCESIE53UC6BDK27KC6BD553UCWSIKK3EHJNCLSIZ?
segment=placement:rubydocorg;)
Top software engineersare using IndeedPrime to land theirdream job.(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSIP27JCWA4YKQWCAADEKQICKBI4Z3JCESIE53UC6BDK27KC6BD553UCWSIKK3EHJNCLSIZ?
segment=placement:rubydocorg;)
ads via Carbon(http://carbonads.net/)
Home (http://yoururlhere.com/) Core (http://yoururlhere.com/core) Std-lib (http://yoururlhere.com/stdlib) Downloads (http://yoururlhere.com/downloads)
Search
FiddleA libffi wrapper for Ruby.
Description (#module-Fiddle-label-Description) (#top)
Fiddle (Fiddle.html) is an extension to translate a foreign function interface (FFI) withruby.
It wraps libffi (http://sourceware.org/libffi/), a popular C library which provides a portableinterface that allows code written in one language to call code written in anotherlanguage.
Example (#module-Fiddle-label-Example) (#top)
Here we will use Fiddle::Function (Fiddle/Function.html) to wrap floor(3) from libm(http://linux.die.net/man/3/floor)
require 'fiddle'
libm = Fiddle.dlopen('/lib/libm.so.6')
floor = Fiddle::Function.new(
libm['floor'],
[Fiddle::TYPE_DOUBLE],
Fiddle::TYPE_DOUBLE
)
puts floor.call(3.14159) #=> 3.0
frozen_string_literal: false
frozen_string_literal: false
frozen_string_literal: false
frozen_string_literal: false
Constants
ALIGN_CHARALIGN_CHARALIGN_CHAR (Fiddle.html#ALIGN_CHAR)
The alignment size of a char
ALIGN_DOUBLEALIGN_DOUBLEALIGN_DOUBLE (Fiddle.html#ALIGN_DOUBLE)
The alignment size of a double
ALIGN_FLOATALIGN_FLOATALIGN_FLOAT (Fiddle.html#ALIGN_FLOAT)
The alignment size of a float
ALIGN_INTALIGN_INTALIGN_INT (Fiddle.html#ALIGN_INT)
The alignment size of an int
ALIGN_INTPTR_TALIGN_INTPTR_TALIGN_INTPTR_T (Fiddle.html#ALIGN_INTPTR_T)
The alignment size of a intptr_t
ALIGN_LONGALIGN_LONGALIGN_LONG (Fiddle.html#ALIGN_LONG)
The alignment size of a long
Class/Module Index
access DLLs
call C functions
-
Home (../index.html) Classes(../index.html#classes) Methods(../index.html#methods)
In Files
fiddle/closure.c
Parent
Object
Methods
::[] (#method-c-5B-5D)::malloc (#method-c-malloc)::new (#method-c-new)::to_ptr (#method-c-to_ptr)#+ (#method-i-2B)#+@ (#method-i-2B-40)#- (#method-i-2D)#-@ (#method-i-2D-40)# (#method-i-3C-3D-3E)#== (#method-i-3D-3D)#[] (#method-i-5B-5D)#[]= (#method-i-5B-5D-3D)#eql? (#method-i-eql-3F)#free (#method-i-free)#free= (#method-i-free-3D)#inspect (#method-i-inspect)#null? (#method-i-null-3F)#ptr (#method-i-ptr)#ref (#method-i-ref)#size (#method-i-size)#size= (#method-i-size-3D)#to_i (#method-i-to_i)#to_int (#method-i-to_int)#to_s (#method-i-to_s)#to_str (#method-i-to_str)#to_value (#method-i-to_value)
Fiddle (../Fiddle.html)Fiddle::BasicTypes (../Fiddle/BasicTypes.html)Fiddle::CParser (../Fiddle/CParser.html)Fiddle::CStruct (../Fiddle/CStruct.html)Fiddle::CStructBuilder (../Fiddle/CStructBuilder.html)Fiddle::CStructEntity (../Fiddle/CStructEntity.html)Fiddle::CUnion (../Fiddle/CUnion.html)Fiddle::CUnionEntity (../Fiddle/CUnionEntity.html)Fiddle::Closure (../Fiddle/Closure.html)Fiddle::Closure::BlockCaller (../Fiddle/Closure/BlockCaller.html)Fiddle::CompositeHandler (../Fiddle/CompositeHandler.html)Fiddle::DLError (../Fiddle/DLError.html)Fiddle::Function (../Fiddle/Function.html)Fiddle::Handle (../Fiddle/Handle.html)Fiddle::Importer (../Fiddle/Importer.html)Fiddle::Pointer (../Fiddle/Pointer.html)Fiddle::Win32Types (../Fiddle/Win32Types.html)
(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSI52JECWB4YKQWCAADEK3JCAADCZ3JCESIE53MFT7IK2QKC6BD553UCABIKK3EHJNCLSIZ?
segment=placement:rubydocorg;)
Top software engineersare using IndeedPrime to land theirdream job.(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSI52JECWB4YKQWCAADEK3JCAADCZ3JCESIE53MFT7IK2QKC6BD553UCABIKK3EHJNCLSIZ?
segment=placement:rubydocorg;)
ads via Carbon(http://carbonads.net/)
(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSI52JECWB4YKQWCAADEK3JCAADCZ3JCESIE53MFT7IK2QKC6BD553UCABIKK3EHJNCLSIZ?
segment=placement:rubydocorg;)
Top software engineersare using IndeedPrime to land theirdream job.(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSI52JECWB4YKQWCAADEK3JCAADCZ3JCESIE53MFT7IK2QKC6BD553UCABIKK3EHJNCLSIZ?
segment=placement:rubydocorg;)
ads via Carbon(http://carbonads.net/)
Home (http://yoururlhere.com/) Core (http://yoururlhere.com/core) Std-lib (http://yoururlhere.com/stdlib) Downloads (http://yoururlhere.com/downloads)
Search
Fiddle::PointerFiddle::Pointer (Pointer.html) is a class to handle C pointers
Public Class Methods
Get the underlying pointer for ruby object val and return it as a Fiddle::Pointer (Pointer.html) object.
Allocate size bytes of memory and associate it with an optional freefunc that will be called when thepointer is garbage collected.
freefunc must be an address pointing to a function or an instance of Fiddle::Function (Function.html)
Create a new pointer to address with an optional size and freefunc.
freefunc will be called when the instance is garbage collected.
Get the underlying pointer for ruby object val and return it as a Fiddle::Pointer (Pointer.html) object.
Public Instance Methods
Returns a new pointer instance that has been advanced n bytes.
Returns a new Fiddle::Pointer (Pointer.html) instance that is a dereferenced pointer for this pointer.
Analogous to the star operator in C.
Returns a new pointer instance that has been moved back n bytes.
Returns a new Fiddle::Pointer (Pointer.html) instance that is a reference pointer for this pointer.
Analogous to the ampersand operator in C.
Returns -1 if less than, 0 if equal to, 1 if greater than other.
Returns nil if ptr cannot be compared to other.
Returns true if other wraps the same pointer, otherwise returns false.
Returns integer stored at index.
Class/Module Index
Fiddle::Pointer[val] => cptrto_ptr(val) => cptr
Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance
Fiddle::Pointer.new(address) => fiddle_cptrnew(address, size) => fiddle_cptrnew(address, size, freefunc) => fiddle_cptr
to_ptr(val) => cptr
ptr + n => new cptr
ptr
ptr - n => new cptr
ref
ptr other => -1, 0, 1, or nil
ptr == other => true or false
ptr[index] an_integerptr[start, length] a_string
MRI stdlib
C pointers
malloc
not portable
-
ruby: c heap
def Fiddle::Pointer.format(value)value.respond_to?(:to_bin) ? value.to_bin : Marshal.dump(value)
end
-
ruby: c heap
require "fiddle"
class Fiddle::PointerNIL = Pointer.new(0)SIZE = Fixnum::SIZE
PACKING_PATTERN = case SIZEwhen 2 then "S"when 4 then "L"when 8 then "Q"end + "!"
def write(value)str = Fiddle::format(value)pad = Fiddle::padding(str)l = pad + str.lengthraise BufferOverflow.new(self, l) if l > sizeself[0, l] = str + 0.chr * padself + l
end
def to_bin[self].pack(PACKING_PATTERN)
endend
-
ruby: c heap
class FixnumSIZE = 1.size
PACKING_PATTERN = case SIZEwhen 2 then "s"when 4 then "l"when 8 then "q"end + "!"
def to_bin[self].pack(PACKING_PATTERN)
end
def self.read_bin(pointer)pointer[0, SIZE].unpack(PACKING_PATTERN).first
endend
-
ruby: c heap
m = Fiddle::Pointer.malloc 64begin m.write(0.chr * 59)
m.write(0.chr * 60)m.write(0.chr * 61)
rescue Fiddle::BufferOverflow => ep e.message
end
"Buffer overflow: 72 bytes at #"
-
ruby: c heap
s = "Hello, Terrible Memory Bank!"i = 4193f = 17.00091
m.write(i)puts m.readq = m.write(-i)puts m.read
q.write(s)puts q.read(String)
r = q.write(s[0, s.length - 1])puts q.read(String)
t = r.write(f)puts r.read(Float)
t.write(-f)puts t.read(Float)
=> 4193
=> -4193
=> Hello, Terrible Memory Bank!
=> Hello, Terrible Memory Bank
=> 17.00091
=> -17.00091
-
stacks
sequential
bounded depth
push & pop
-
sequential
push data on
pop data off
-
contiguous
bounded depth
-
c: array stack
fixed size
malloc to create
realloc to resize
#include #define STACK_MAX 100
typedef enum {STACK_OK = 0,STACK_OVERFLOW,STACK_UNDERFLOW
} STACK_STATUS;
typedef struct stack STACK;struct stack {
int data[STACK_MAX];int size;
};
STACK *NewStack() {STACK *s;s = malloc(sizeof(STACK));s->size = 0;return s;
}
int depth(STACK *s) {return s->size;
}
STACK_STATUS push(STACK *s, int data) {if (s->size < STACK_MAX) {
s->data[s->size++] = data;return STACK_OK;
}return STACK_OVERFLOW;
}
STACK_STATUS pop(STACK *s, int *r) {if (s->size > 0) {
*r = s->data[s->size - 1];s->size--;return STACK_OK;
}return STACK_UNDERFLOW;
}
-
ruby: array stack
core class
stack semantics
s = []s.push(1)s.push(3)
puts "depth = #{s.length}"l = s.popr = s.popputs "#{l} + #{r} = #{l + r}"puts "depth = #{s.length}"
-
singly linked list
functional
shared elements
immutability
-
c: cactus stack
nil is empty
grows on push
manual GC
#include
typedef struct stack STACK;struct stack {
int data;STACK *next;
};
STACK *push(STACK *s, int data) {STACK *r = malloc(sizeof(STACK));r->data = data;r->next = s;return r;
}
STACK *pop(STACK *s, int *r) {if (s == NULL)
exit(1);*r = s->data;return s->next;
}
int depth(STACK *s) {int r = 0;for (STACK *t = s; t != NULL; t = t->next) {
r++;}return r;
}
void gc(STACK **old, int items) {STACK *t;for (; items > 0 && *old != NULL; items--) {
t = *old;*old = (*old)->next;free(t);
}}
-
c: cactus stack
nil is empty
grows on push
manual GC
#include #include
int sum(STACK *tos) {int a = 0;for (int p = 0; tos != NULL;) {
tos = pop(tos, &p);a += p;
}return a;
}
void print_sum(STACK *s) {printf("%d items: sum = %d\n", depth(s), sum(s));
}
int main() {STACK *s1 = push(NULL, 7);STACK *s2 = push(push(s1, 7), 11);s1 = push(push(push(s1, 2), 9), 4);
STACK *s3 = push(s1, 17);s1 = push(s1, 3);print_sum(s1);print_sum(s2);print_sum(s3);
}
-
ruby: cactus stack
nil is empty
grows on push
automatic GC
class Stackinclude Enumerableattr_reader :head, :tail
def initialize data, tail = nil@head = data@tail = tail || EmptyStack.new
end
def push itemStack.new item, self
end
def pop[head, tail]
end
def eacht = selfuntil t.is_a?(EmptyStack)
yield t.headt = t.tail
endend
end
class EmptyStackinclude Enumerable
def push itemStack.new item
end
def pop[nil, self]
end
def each; endend
-
caches
word-aligned
discontiguous
label-addressable
-
c: hash map
array
associative arrays
search
#include
struct map {int size;assoc_array_t **chains;
};typedef struct map map_t;
map_t *map_new(int size) {map_t *m = malloc(sizeof(map_t));m->chains = malloc(sizeof(assoc_array_t*) * size);for (int i = 0; i < size; i++) {
m->chains[i] = NULL;}m->size = size;return m;
}
int map_chain(map_t *m, char *k) {unsigned long int b;for (int i = strlen(k) - 1; b < ULONG_MAX && i > 0; i--) {
b = b size;
}
char *map_get(map_t *m, char *k) {search_t *s = search_find(m->chains[map_chain(m, k)], k);if (s != NULL) {
return s->value;}return NULL;
}
void map_set(map_t *m, char *k, char *v) {int b = map_chain(m, k);assoc_array_t *a = m->chains[b];search_t *s = search_find(a, k);if (s->value != NULL) {
s->cursor->value = strdup(v);} else {
assoc_array_t *n = assoc_array_new(k, v);if (s->cursor == a) {
n->next = s->cursor;m->chains[b] = n;
} else if (s->cursor == NULL) {s->memo->next = n;
} else {n->next = s->cursor;s->memo->next = n;
}}free(s);
}
-
c: hash map
array
associative arrays
search
#include #include
struct assoc_array {char *key;void *value;struct assoc_array *next;
};typedef struct assoc_array assoc_array_t;
assoc_array_t *assoc_array_new(char *k, char *v) {assoc_array_t *a = malloc(sizeof(assoc_array_t));a->key = strdup(k);a->value = strdup(v);a->next = NULL;return a;
}
char *assoc_array_get_if(assoc_array_t *a, char *k) {char *r = NULL;if (a != NULL && strcmp(a->key, k) == 0) {
r = strdup(a->value);}return r;
}
-
c: hash map
array
associative arrays
search
struct search {char *term, *value;assoc_array_t *cursor, *memo;
};typedef struct search search_t;
search_t *search_new(assoc_array_t *a, char *k) {search_t *s = malloc(sizeof(search_t));s->term = k;s->value = NULL;s->cursor = a;s->memo = NULL;return s;
}
void search_step(search_t *s) {s->value = assoc_array_get_if(s->cursor, s->term);
}
int searching(search_t *s) {return s->value == NULL && s->cursor != NULL;
}
search_t *search_find(assoc_array_t *a, char *k) {search_t *s = search_new(a, k);for (search_step(s); searching(s); search_step(s)) {
s->memo = s->cursor;s->cursor = s->cursor->next;
}return s;
}
-
c: hash map
array
associative arrays
search
#include #include map.h
int main( int argc, char **argv ) {map_t *m = map_new(1024);map_set(m, "apple", "rosy");printf("%s\n", map_get(m, "apple"));map_set(m, "blueberry", "sweet");printf("%s\n", map_get(m, "blueberry"));map_set(m, "cherry", "pie");printf("%s\n", map_get(m, "cherry"));map_set(m, "cherry", "tart");printf("%s\n", map_get(m, "cherry"));printf("%s\n", map_get(m, tart"));
}
rosy
sweet
pie
tart(null)
-
ruby: hash map
core class
m = {"apple": "rosy"}puts(m["apple"])
m["blueberry"] = "sweet"puts m["blueberry"]
m["cherry"] = "pie"puts m["cherry"]
m["cherry"] = "tart"puts m["cherry"]
puts m["tart"]
rosy
sweet
pie
tart
-
This repository Pull requests Issues Gist
No description or website provided.
demo + Switched to install for Makefile. 11 years ago
lib Added note about using absolute paths to rootdir rdoc 8 months ago
test Added assertion to quell minitest saying I'm not testing something 3 years ago
tutorial - Fixed examples for 1.9 compatibility. 5 years ago
History.txt prepped for release 2 years ago
Manifest.txt Removed inline_package in favor of hoe's packaging. 9 years ago
README.txt - Fixed code/home urls in readme/gem. 4 years ago
Rakefile isolated and cleaned dependencies 5 years ago
example.rb - Clean up example.rb and force removal of ~/.ruby_inline 7 years ago
example2.rb - Fixed examples for 1.9 compatibility. 5 years ago
Search
seattlerb / rubyinline
Code Issues 0 Pull requests 0 Projects 0 Wiki Pulse Graphs
176 commits 1 branch 0 releases 3 contributors
Clone or downloadClone or download Create new file Upload files Find file master Branch: New pull request
Latest commit 112e6ad on 31 Mar zenspider Added note about using absolute paths to rootdir rdoc
README.txt
= Ruby Inline
rdoc :: http://docs.seattlerb.org/RubyInline/home :: http://www.zenspider.com/ZSS/Products/RubyInline/code :: https://github.com/seattlerb/rubyinline
== DESCRIPTION:
Inline allows you to write foreign code within your ruby code. Itautomatically determines if the code in question has changed andbuilds it only when necessary. The extensions are then automaticallyloaded into the class/module that defines it.
You can even write extra builders that will allow you to write inlinedcode in any language. Use Inline::C as a template and look atModule#inline for the required API.
== PACKAGING:
To package your binaries into a gem, use hoe's INLINE andFORCE_PLATFORM env vars.
Example:
rake package INLINE=1
or:
rake package INLINE=1 FORCE_PLATFORM=mswin32
See hoe for more details.
== FEATURES/PROBLEMS:
* Quick and easy inlining of your C or C++ code embedded in your ruby script.
191 3115 Watch Star Fork
rubyinline
inline c
-
This repository Pull requests Issues Gist
No description or website provided.
lib prepped for release a year ago
test + Switched to minitest 8 years ago
History.txt prepped for release a year ago
Manifest.txt prepped for release 8 years ago
README.txt Updated with better examples 8 years ago
Rakefile Skip all 2.x 2 years ago
bench.rb + Improved benchmark code 8 years ago
example.rb - Fixed defasm to work much better. 8 years ago
Search
seattlerb / wilson
Code Issues 1 Pull requests 0 Projects 0 Wiki Pulse Graphs
27 commits 1 branch 0 releases 2 contributors
Clone or downloadClone or download Create new file Upload files Find file master Branch: New pull request
Latest commit 82d990f on 26 Oct 2015 zenspider prepped for release
README.txt
= wilson
* http://rubyforge.org/projects/seattlerb
== DESCRIPTION:
Wilson is a pure ruby x86 assembler. No, really. Worst Idea Evar.
Why "wilson"? I wanted to name it "metal", but there is an existingproject with that name... So I'm naming it after Wilson Bilkovich, whois about as metal as you can get (and it is easier to spell than"bilkovich", even tho that sounds more metal).
== FEATURES/PROBLEMS:
* Generates x86 machine code directly. No dependencies. No system calls.* Registers ruby methods with #defasm, or run inline assembly with #asm.* Terrible, yet, awesome.
== SYNOPSIS:
class X defasm :superfast_meaning_of_life do eax.mov 42 to_ruby eax # ruby fixnums = (n
-
This repository Pull requests Issues Gist
NeverSayDie will let you rescue from SEGV's and is evil
ext/neversaydie Changed Config:: for RbConfig 2 years ago
lib making class declarations line up 7 years ago
test updating rdoc, removing binfile 7 years ago
.autotest adding extconf stuff 7 years ago
.gitignore adding pkg to git ignore 7 years ago
CHANGELOG.ja.rdoc translating stuff 7 years ago
CHANGELOG.rdoc adding extconf stuff 7 years ago
Manifest.txt updating manifest 7 years ago
README.ja.rdoc Natural Japanese 7 years ago
README.rdoc Added documentation for debian 2 years ago
Rakefile Rakefile + gemspec fixes 2 years ago
neversaydie.gemspec Rakefile + gemspec fixes 2 years ago
Search
tenderlove / neversaydie
Code Issues 1 Pull requests 1 Projects 0 Wiki Pulse Graphs
18 commits 1 branch 0 releases 3 contributors
Clone or downloadClone or download Create new file Upload files Find file master Branch: New pull request
Latest commit 1766194 on 12 Jun 2015 tenderlove Merge pull request #3 from cortexmedia/master
README.rdoc
NeverSayDieseattlerb.rubyforge.org
github.com/tenderlove/neversaydie
DESCRIPTION:
NEVER SAY DIE lets you rescue from segmentation faults. Got a SEGV, don't worry about it anymore! Just rescue an
exception and get on with life. Who cares about getting a SEGV anyway? It's just memory. I mean, when I was in school, I
didn't need 100% to pass the class. Why should your memory need to be 100% correct to get the job done? A little memory
corruption here and there doesn't hurt anyone.
So go for it! Kick back, grab a beer, require the NEVER SAY DIE gem and let your problems go away sometimes!
FEATURES/PROBLEMS:
Oh so many problems
Portability
Not solving the root cause..
It might not work.
35 82 Watch Unstar Fork
never day die
libsigsegfault
-
SIMD
branch prediction
cache misses
memory latency