convert cgo function pointer call to repeated iteration

This commit is contained in:
mappu 2017-05-20 18:01:55 +12:00
parent be594cb1a5
commit 3911d7d66c
3 changed files with 118 additions and 62 deletions

123
main.go
View File

@ -68,61 +68,102 @@ func withBoltDBReference(b ObjectReference, fn func(db *bolt.DB) error) error {
return fn(ptrDB) return fn(ptrDB)
} }
type CallResponse struct {
s string
e error
}
type NextCall struct {
content chan CallResponse
}
//export Bolt_ListBuckets //export Bolt_ListBuckets
func Bolt_ListBuckets(b ObjectReference, browse []string, ctx uintptr, withEach func(uintptr, string)) string { func Bolt_ListBuckets(b ObjectReference, browse []string) ObjectReference {
if len(browse) == 0 { pNC := &NextCall{
return Bolt_ListBucketsAtRoot(b, ctx, withEach) content: make(chan CallResponse, 0),
} }
err := withBoltDBReference(b, func(db *bolt.DB) error { pNC_Ref := gms.Put(pNC)
return db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(browse[0])) go func() {
if bucket == nil { err := withBoltDBReference(b, func(db *bolt.DB) error {
return errors.New("Unknown bucket") return db.View(func(tx *bolt.Tx) error {
}
if len(browse) == 0 {
// Root-mode
return tx.ForEach(func(k []byte, _ *bolt.Bucket) error {
pNC.content <- CallResponse{s: string(k)}
return nil
})
} else {
// Nested-mode
bucket := tx.Bucket([]byte(browse[0]))
if bucket == nil {
return errors.New("Unknown bucket")
}
for i := 1; i < len(browse); i += 1 {
bucket = bucket.Bucket([]byte(browse[i]))
if bucket == nil {
return errors.New("Unknown bucket")
}
}
return bucket.ForEach(func(k, v []byte) error {
pNC.content <- CallResponse{s: string(k)}
return nil
})
for i := 1; i < len(browse); i += 1 {
bucket = bucket.Bucket([]byte(browse[i]))
if bucket == nil {
return errors.New("Unknown bucket")
} }
}
return bucket.ForEach(func(k, v []byte) error {
withEach(ctx, string(k))
return nil
}) })
}) })
})
if err != nil { if err != nil {
return err.Error() pNC.content <- CallResponse{e: err}
}
close(pNC.content)
}()
return pNC_Ref
}
const (
ERROR_AND_STOP_CALLING int = 100
ERROR_AND_KEEP_CALLING = 101
FINISHED_OK = 102
REAL_MESSAGE = 103
)
//export GetNext
func GetNext(oRef ObjectReference) (int, string) {
pNC_Iface, ok := gms.Get(oRef)
if !ok {
return ERROR_AND_STOP_CALLING, NullObjectReference.Error()
} }
return "" pNC, ok := pNC_Iface.(*NextCall)
if !ok {
return ERROR_AND_STOP_CALLING, NullObjectReference.Error()
}
cr, ok := <-pNC.content
if !ok {
gms.Delete(oRef)
return FINISHED_OK, ""
}
if cr.e != nil {
return ERROR_AND_KEEP_CALLING, cr.e.Error()
}
return REAL_MESSAGE, cr.s
} }
//export Bolt_ListBucketsAtRoot //export Bolt_ListBucketsAtRoot
func Bolt_ListBucketsAtRoot(b ObjectReference, ctx uintptr, withEach func(uintptr, string)) string { func Bolt_ListBucketsAtRoot(b ObjectReference) ObjectReference {
err := withBoltDBReference(b, func(db *bolt.DB) error { return Bolt_ListBuckets(b, nil)
return db.View(func(tx *bolt.Tx) error {
return tx.ForEach(func(n []byte, bucket *bolt.Bucket) error {
withEach(ctx, string(n))
return nil
})
})
})
if err != nil {
return err.Error()
}
return ""
} }
//export Bolt_Close //export Bolt_Close

12
qbolt.h
View File

@ -68,9 +68,17 @@ struct Bolt_Open_return {
extern struct Bolt_Open_return Bolt_Open(GoString p0, GoUint32 p1, GoInt64 p2); extern struct Bolt_Open_return Bolt_Open(GoString p0, GoUint32 p1, GoInt64 p2);
extern GoString Bolt_ListBuckets(GoInt64 p0, GoSlice p1, GoUintptr p2, void* p3); extern GoInt64 Bolt_ListBuckets(GoInt64 p0, GoSlice p1);
extern GoString Bolt_ListBucketsAtRoot(GoInt64 p0, GoUintptr p1, void* p2); /* Return type for GetNext */
struct GetNext_return {
GoInt r0;
GoString r1;
};
extern struct GetNext_return GetNext(GoInt64 p0);
extern GoInt64 Bolt_ListBucketsAtRoot(GoInt64 p0);
extern GoString Bolt_Close(GoInt64 p0); extern GoString Bolt_Close(GoInt64 p0);

View File

@ -23,32 +23,39 @@ BoltDB* BoltDB::createFrom(QString filePath, QString &errorOut)
return ret; return ret;
} }
template<typename T> static const int ERROR_AND_STOP_CALLING = 100;
struct Wrapper { static const int ERROR_AND_KEEP_CALLING = 101;
T t; static const int FINISHED_OK = 102;
}; static const int REAL_MESSAGE = 103;
static void byteArrayCallback(void* ctx, GoString text) {
auto fn = static_cast<Wrapper<NameReciever>*>(ctx);
fn->t(QByteArray::fromRawData(text.p, text.n));
}
bool BoltDB::listBucketsAtRoot(QString& errorOut, NameReciever cb) bool BoltDB::listBucketsAtRoot(QString& errorOut, NameReciever cb)
{ {
auto containerVar = Wrapper<NameReciever>{cb}; auto listJob = ::Bolt_ListBucketsAtRoot(this->gmsDbRef);
GoString err = ::Bolt_ListBucketsAtRoot( errorOut.clear();
this->gmsDbRef,
(GoUintptr)(&containerVar),
byteArrayCallback // -fpermissive
);
if (err.n > 0) { for(;;) {
errorOut = QString(err.p); auto gnr = ::GetNext(listJob);
return false;
if (gnr.r0 == ERROR_AND_STOP_CALLING) {
errorOut.append(QString(gnr.r1.p)); // log error
break; // done
} else if (gnr.r0 == ERROR_AND_KEEP_CALLING) {
errorOut.append(QString(gnr.r1.p)); // log error
continue;
} else if (gnr.r0 == FINISHED_OK) {
// Once we hit this, the go-side will clean up the channel / associated goroutines
break;
} else if (gnr.r0 == REAL_MESSAGE) {
cb(QByteArray(gnr.r1.p, gnr.r1.n));
continue;
}
} }
return true; return (errorOut.length() == 0);
} }
BoltDB::~BoltDB() BoltDB::~BoltDB()