factor/extra/forestdb/lib/lib.factor

403 lines
13 KiB
Factor

! Copyright (C) 2014 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: accessors alien.c-types alien.data alien.strings arrays
classes.struct combinators constructors continuations
destructors forestdb.ffi forestdb.paths fry generalizations
io.encodings.string io.encodings.utf8 io.pathnames kernel libc
math multiline namespaces sequences ;
QUALIFIED: sets
IN: forestdb.lib
/*
! Issues
! 2) build on macosx doesn't search /usr/local for libsnappy
! 3) build on macosx doesn't include -L/usr/local/lib when it finds snappy
! - link_directories(/usr/local/lib) or some other fix
! 4) byseq iteration doesn't have bodies, weird.
! 5) Get byseq ignores seqnum and uses key instead if key is set
*/
ERROR: fdb-error error ;
: fdb-check-error ( ret -- )
dup FDB_RESULT_SUCCESS = [ drop ] [ fdb-error ] if ;
TUPLE: fdb-kvs-handle < disposable handle ;
: <fdb-kvs-handle> ( handle -- obj )
fdb-kvs-handle new-disposable
swap >>handle ; inline
M: fdb-kvs-handle dispose*
handle>> fdb_kvs_close fdb-check-error ;
TUPLE: fdb-file-handle < disposable handle ;
: <fdb-file-handle> ( handle -- obj )
fdb-file-handle new-disposable
swap >>handle ; inline
M: fdb-file-handle dispose*
handle>> fdb_close fdb-check-error ;
SYMBOL: current-fdb-file-handle
SYMBOL: current-fdb-kvs-handle
: get-kvs-default-config ( -- kvs-config )
S{ fdb_kvs_config
{ create_if_missing t }
{ custom_cmp f }
} clone ;
: fdb-open-kvs' ( file-handle fdb-kvs-handle kvs-config -- file-handle handle )
[ dup handle>> ] 2dip
[ handle>> ] dip
[ fdb_kvs_open_default fdb-check-error ] 2keep drop
void* deref <fdb-kvs-handle> ;
: fdb-open-kvs ( fdb-file-handle kvs-config -- file-handle handle )
[ f void* <ref> <fdb-kvs-handle> ] dip fdb-open-kvs' ;
: fdb-open ( path config kvs-config -- file-handle handle )
[
[ f void* <ref> ] 2dip
[ absolute-path ensure-fdb-filename-directory ] dip
[ fdb_open fdb-check-error ] 3keep
2drop void* deref <fdb-file-handle>
] dip fdb-open-kvs ;
: fdb-open-default-config ( path -- file-handle handle )
fdb_get_default_config get-kvs-default-config fdb-open ;
: ret>string ( void** len -- string )
[ void* deref ] [ size_t deref ] bi*
[ memory>byte-array utf8 decode ] [ drop (free) ] 2bi ;
: get-file-handle ( -- handle )
current-fdb-file-handle get handle>> ;
: get-kvs-handle ( -- handle )
current-fdb-kvs-handle get handle>> ;
: fdb-set-kv ( key value -- )
[ get-kvs-handle ] 2dip
[ dup length ] bi@ fdb_set_kv fdb-check-error ;
: <key-doc> ( key -- doc )
fdb_doc malloc-struct
swap [ utf8 malloc-string >>key ] [ length >>keylen ] bi ;
: <seqnum-doc> ( seqnum -- doc )
fdb_doc malloc-struct
swap >>seqnum ;
! Fill in document by exemplar
: fdb-get ( doc -- doc )
[ get-kvs-handle ] dip [ fdb_get fdb-check-error ] keep ;
: fdb-get-metaonly ( doc -- doc )
[ get-kvs-handle ] dip [ fdb_get_metaonly fdb-check-error ] keep ;
: fdb-get-byseq ( doc -- doc )
[ get-kvs-handle ] dip [ fdb_get_byseq fdb-check-error ] keep ;
: fdb-get-metaonly-byseq ( doc -- doc )
[ get-kvs-handle ] dip [ fdb_get_metaonly_byseq fdb-check-error ] keep ;
: fdb-get-byoffset ( doc -- doc )
[ get-kvs-handle ] dip [ fdb_get_byoffset fdb-check-error ] keep ;
! Set/delete documents
: fdb-set ( doc -- )
[ get-kvs-handle ] dip fdb_set fdb-check-error ;
: fdb-del ( doc -- )
[ get-kvs-handle ] dip fdb_del fdb-check-error ;
: fdb-get-kv ( key -- value/f )
[ get-kvs-handle ] dip
dup length f void* <ref> 0 size_t <ref>
[ fdb_get_kv ] 2keep
rot {
{ FDB_RESULT_SUCCESS [ ret>string ] }
{ FDB_RESULT_KEY_NOT_FOUND [ 2drop f ] }
[ fdb-error ]
} case ;
: fdb-del-kv ( key -- )
[ get-kvs-handle ] dip dup length fdb_del_kv fdb-check-error ;
: fdb-doc-create ( key meta body -- doc )
[ f void* <ref> ] 3dip
[ dup length ] tri@
[ fdb_doc_create fdb-check-error ] 7 nkeep 6 ndrop
void* deref fdb_doc memory>struct ;
: fdb-doc-update ( doc meta body -- )
[ void* <ref> ] 2dip
[ dup length ] bi@
fdb_doc_update fdb-check-error ;
: fdb-doc-free ( doc -- )
fdb_doc_free fdb-check-error ;
: clear-doc-key ( doc -- doc )
[ dup [ (free) f ] when ] change-key
0 >>keylen ;
: with-doc ( doc quot: ( doc -- ) -- )
over '[ _ _ [ _ fdb-doc-free rethrow ] recover ] call ; inline
: with-create-doc ( key meta body quot: ( doc -- ) -- )
[ fdb-doc-create ] dip with-doc ; inline
: fdb-get-info ( -- fdb_file_info )
get-file-handle
fdb_file_info <struct> [ fdb_get_file_info fdb-check-error ] keep ;
: fdb-get-kvs-info ( -- fdb_kvs_info )
get-kvs-handle
fdb_kvs_info <struct> [ fdb_get_kvs_info fdb-check-error ] keep ;
: fdb-commit ( fdb_commit_opt_t -- )
[ get-file-handle ] dip fdb_commit fdb-check-error ;
: fdb-maybe-commit ( fdb_commit_opt_t/f -- )
[ fdb-commit ] when* ;
: fdb-commit-normal ( -- ) FDB_COMMIT_NORMAL fdb-commit ;
: fdb-commit-wal-flush ( -- ) FDB_COMMIT_MANUAL_WAL_FLUSH fdb-commit ;
: fdb-compact ( new-path -- )
[ get-file-handle ] dip absolute-path
fdb_compact fdb-check-error ;
: fdb-compact-commit ( new-path -- )
fdb-compact fdb-commit-wal-flush ;
: fdb-swap-current-db ( new-path -- )
current-fdb-kvs-handle [ dispose f ] change
fdb-open-default-config
[ current-fdb-file-handle set ]
[ current-fdb-kvs-handle set ] bi* ;
: fdb-compact-and-swap-db ( path -- )
next-vnode-version-name
[ fdb-compact fdb-commit-wal-flush ]
[ fdb-swap-current-db ] bi ;
! Call from within with-foresdb
: fdb-open-snapshot ( seqnum -- handle )
[
get-kvs-handle
f void* <ref>
] dip [
fdb_snapshot_open fdb-check-error
] 2keep drop void* deref <fdb-kvs-handle> ;
! fdb_rollback returns a new handle, so we
! have to replace our current handle with that one
! XXX: can't call dispose on old handle, library handles that
: fdb-rollback ( seqnum -- )
[ get-kvs-handle void* <ref> ] dip
[ fdb_rollback fdb-check-error ] 2keep drop
void* deref <fdb-kvs-handle> current-fdb-kvs-handle set ;
TUPLE: fdb-iterator < disposable handle ;
: <fdb-iterator> ( handle -- obj )
fdb-iterator new-disposable
swap >>handle ; inline
M: fdb-iterator dispose*
handle>> fdb_iterator_close fdb-check-error ;
: fdb-iterator-init ( start-key end-key fdb_iterator_opt_t -- iterator )
[ get-kvs-handle f void* <ref> ] 3dip
[ [ dup length ] bi@ ] dip
[ fdb_iterator_init fdb-check-error ] 7 nkeep 5 ndrop nip
void* deref <fdb-iterator> ;
: fdb-iterator-byseq-init ( start-seq end-seq fdb_iterator_opt_t -- iterator )
[ get-kvs-handle f void* <ref> ] 3dip
[ fdb_iterator_sequence_init fdb-check-error ] 5 nkeep 3 ndrop nip
void* deref <fdb-iterator> ;
: fdb-iterator-init-none ( start-key end-key -- iterator )
FDB_ITR_NONE fdb-iterator-init ;
: fdb-iterator-meta-only ( start-key end-key -- iterator )
FDB_ITR_METAONLY fdb-iterator-init ;
: fdb-iterator-no-deletes ( start-key end-key -- iterator )
FDB_ITR_NO_DELETES fdb-iterator-init ;
: check-iterate-result ( doc fdb_status -- doc/f )
{
{ FDB_RESULT_SUCCESS [ void* deref fdb_doc memory>struct ] }
{ FDB_RESULT_ITERATOR_FAIL [ drop f ] }
[ throw ]
} case ;
: fdb-iterate ( iterator word -- doc )
'[
fdb_doc malloc-struct fdb_doc <ref>
[ _ execute ] keep swap check-iterate-result
] call ; inline
! fdb_doc key, meta, body only valid inside with-forestdb
! so make a helper word to preserve them outside
TUPLE: fdb-doc seqnum keylen key metalen meta bodylen body deleted? offset size-ondisk ;
CONSTRUCTOR: <fdb-doc> fdb-doc ( seqnum keylen key metalen meta bodylen body deleted? offset size-ondisk -- obj ) ;
TUPLE: fdb-info filename new-filename doc-count space-used file-size ;
CONSTRUCTOR: <info> fdb-info ( filename new-filename doc-count space-used file-size -- obj ) ;
/*
! Example fdb_doc and converted doc
S{ fdb_doc
{ keylen 4 } { metalen 0 } { bodylen 4 } { size_ondisk 0 }
{ key ALIEN: 1002f2f10 } { seqnum 5 } { offset 4256 }
{ meta ALIEN: 1002dc790 } { body f } { deleted f }
}
T{ doc
{ seqnum 5 }
{ keylen 4 } { key "key5" }
{ metalen 0 } { bodylen 4 }
{ offset 4256 } { size-ondisk 0 }
}
*/
: alien/length>string ( alien n -- string/f )
[ drop f ] [
over [
memory>byte-array utf8 decode
] [
2drop f
] if
] if-zero ;
: fdb_doc>doc ( fdb_doc -- doc )
{
[ seqnum>> ]
[ keylen>> ]
[ [ key>> ] [ keylen>> ] bi alien/length>string ]
[ metalen>> ]
[ [ meta>> ] [ metalen>> ] bi alien/length>string ]
[ bodylen>> ]
[ [ body>> ] [ bodylen>> ] bi alien/length>string ]
[ deleted>> >boolean ]
[ offset>> ]
[ size_ondisk>> ]
} cleave <fdb-doc> ;
: fdb_file_info>info ( fdb_doc -- doc )
{
[ filename>> alien>native-string ]
[ new_filename>> alien>native-string ]
[ doc_count>> ]
[ space_used>> ]
[ file_size>> ]
} cleave <info> ;
: fdb-iterator-prev ( iterator -- doc/f ) \ fdb_iterator_prev fdb-iterate ;
: fdb-iterator-next ( iterator -- doc/f ) \ fdb_iterator_next fdb-iterate ;
: fdb-iterator-next-meta-only ( iterator -- doc/f ) \ fdb_iterator_next_metaonly fdb-iterate ;
: fdb-iterator-seek ( iterator key -- )
dup length fdb_iterator_seek fdb-check-error ;
: with-fdb-iterator ( start-key end-key fdb_iterator_opt_t iterator-init iterator-next quot: ( obj -- ) -- )
[ execute ] 2dip
'[
_ &dispose handle>> [
_ execute [ _ with-doc t ] [ f ] if*
] curry loop
] with-destructors ; inline
<PRIVATE
: collector-for-when ( quot exemplar -- quot' vec )
[ length ] keep new-resizable [ [ over [ push ] [ 2drop ] if ] curry compose ] keep ; inline
: collector-when ( quot -- quot' vec )
V{ } collector-for-when ; inline
PRIVATE>
: with-fdb-map ( start-key end-key fdb_iterator_opt_t iterator-init iterator-next quot: ( obj -- ) -- )
[ execute ] 2dip
'[
_ &dispose handle>> [
_ execute [ _ with-doc t ] [ f f ] if* swap
] curry collector-when [ loop ] dip
] with-destructors ; inline
: with-fdb-normal-iterator ( start-key end-key quot -- )
[ FDB_ITR_NONE \ fdb-iterator-init \ fdb-iterator-next ] dip
with-fdb-iterator ; inline
: with-fdb-normal-meta-iterator ( start-key end-key quot -- )
[ FDB_ITR_NONE \ fdb-iterator-init \ fdb-iterator-next-meta-only ] dip
with-fdb-iterator ; inline
: with-fdb-byseq-each ( start-seq end-seq quot -- )
[ FDB_ITR_NONE \ fdb-iterator-byseq-init \ fdb-iterator-next-meta-only ] dip
with-fdb-iterator ; inline
: with-fdb-byseq-map ( start-seq end-seq quot -- )
[ FDB_ITR_NONE \ fdb-iterator-byseq-init \ fdb-iterator-next-meta-only ] dip
with-fdb-map ; inline
! Do not try to commit here, as it will fail with FDB_RESULT_RONLY_VIOLATION
! fdb-current is weird, it gets replaced if you call fdb-rollback
! Therefore, only clean up fdb-current once, and clean it up at the end
: with-forestdb-handles ( file-handle handle quot fdb_commit_opt_t/f -- )
'[
_ current-fdb-file-handle [
_ current-fdb-kvs-handle [
[
@
_ fdb-maybe-commit
current-fdb-file-handle get &dispose drop
current-fdb-kvs-handle get &dispose drop
] [
[
current-fdb-file-handle get &dispose drop
current-fdb-kvs-handle get &dispose drop
] with-destructors
rethrow
] recover
] with-variable
] with-variable
] with-destructors ; inline
! XXX: When you don't commit-wal at the end of with-forestdb, it won't
! persist to disk for next time you open the db.
: with-forestdb-handles-commit-normal ( file-handle handle quot commit -- )
FDB_COMMIT_NORMAL with-forestdb-handles ; inline
: with-forestdb-handles-commit-wal ( file-handle handle quot commit -- )
FDB_COMMIT_MANUAL_WAL_FLUSH with-forestdb-handles ; inline
: with-forestdb-snapshot ( n quot -- )
[ fdb-open-snapshot ] dip '[
_ current-fdb-kvs-handle [
[
@
current-fdb-kvs-handle get &dispose drop
] [
current-fdb-kvs-handle get [ &dispose drop ] when*
rethrow
] recover
] with-variable
] with-destructors ; inline
: with-forestdb-path ( path quot -- )
[ absolute-path fdb-open-default-config ] dip with-forestdb-handles-commit-wal ; inline
! [ absolute-path fdb-open-default-config ] dip with-forestdb-handle-commit-normal ; inline