It been a while since I mentioned this idea, but here’s an implementation of a memcached client in Factor. It’s not completely robust, but it works well enough. Next up is a server that accepts Factor scripts to execute locally.
USING: kernel io io.crlf io.encodings.ascii io.sockets io.streams.duplex strings sequences splitting math.parser formatting ;
IN: memcached.client
! Based on protocol for memcached v1.2.2
<PRIVATE
: get-response-data ( int -- string )
read read-crlf drop ;
: get-response-header ( string -- int )
" " split fourth string>number ;
! I'm sure there's a better way to do this
: get-response-end ( -- string/f )
read-crlf [ "END" = not ] keep and ;
: get-response ( -- seq )
V{ } [ get-response-end dup ] [ get-response-header get-response-data suffix ] while drop ;
: store-command ( command key value -- string )
[ length ] keep "%s %s 0 0 %d\r\n%s\r\n" sprintf ;
: send ( cmd -- )
write flush ;
: store ( command key value stream -- string )
[ store-command send read-crlf ] with-stream* ;
: retrieve ( str stream -- data )
[ send get-response ] with-stream* ;
: remove ( command stream -- string )
[ send read-crlf ] with-stream* ;
PRIVATE>
! Handling connections
: connect ( ip port -- stream local )
<inet> ascii <client> ;
: disconnect ( stream -- )
drop ;
! Storage commands: set, add, replace
: set ( key data stream -- str )
[ "set" -rot ] dip store ;
: add ( key data stream -- str )
[ "add" -rot ] dip store ;
: replace ( key data stream -- str )
[ "replace" -rot ] dip store ;
! Retrieval commands: get
: get ( key stream -- data )
[ "get %s\r\n" sprintf ] dip retrieve ;
! Deletion commands: delete
: delete ( key stream -- str )
[ "delete %s\r\n" sprintf ] dip remove ;
! Increment/Decrement
: incr ( key val stream -- str )
[ "incr %s %d\r\n" sprintf send read-crlf ] with-stream* ;
: decr ( key val stream -- str )
[ "decr %s %d\r\n" sprintf send read-crlf ] with-stream* ;