diff --git a/lib9p/lib9p.go b/lib9p/lib9p.go index 4a9b068..3d83567 100644 --- a/lib9p/lib9p.go +++ b/lib9p/lib9p.go @@ -14,6 +14,7 @@ package main import "C" import ( + "io" "strings" "sync" "unsafe" @@ -27,10 +28,20 @@ type connHandle struct { fsys *client.Fsys } +// File handle - for streaming reads +type fidHandle struct { + conn C.int // parent connection handle + reader io.ReadCloser +} + var ( handleMu sync.Mutex handles = make(map[C.int]*connHandle) nextHandle C.int = 1 + + fidMu sync.Mutex + fids = make(map[C.int]*fidHandle) + nextFid C.int = 1 ) func allocHandle(h *connHandle) C.int { @@ -54,6 +65,27 @@ func freeHandle(id C.int) { delete(handles, id) } +func allocFid(f *fidHandle) C.int { + fidMu.Lock() + defer fidMu.Unlock() + id := nextFid + nextFid++ + fids[id] = f + return id +} + +func getFid(id C.int) *fidHandle { + fidMu.Lock() + defer fidMu.Unlock() + return fids[id] +} + +func freeFid(id C.int) { + fidMu.Lock() + defer fidMu.Unlock() + delete(fids, id) +} + // Error handling - last error per thread (simplified: global for now) var lastError string var lastErrMu sync.Mutex @@ -361,6 +393,75 @@ func ollie9p_rename(handle C.int, oldPath *C.char, newName *C.char) C.int { return 0 } +// --- Streaming API --- + +// ollie9p_open opens a file for streaming reads. +// Returns: fid handle > 0 on success, -1 on error. +// The fid must be closed with ollie9p_close_fid. +// +//export ollie9p_open +func ollie9p_open(handle C.int, path *C.char) C.int { + h := getHandle(handle) + if h == nil { + setError(&errString{"invalid handle"}) + return -1 + } + + reader, err := h.fsys.OpenRead(C.GoString(path)) + if err != nil { + setError(err) + return -1 + } + + fid := &fidHandle{ + conn: handle, + reader: reader, + } + setError(nil) + return allocFid(fid) +} + +// ollie9p_read_fid reads from an open file into a buffer. +// Returns: bytes read (0 on EOF), -1 on error. +// Blocks until data is available or EOF. +// +//export ollie9p_read_fid +func ollie9p_read_fid(fid C.int, buf *C.char, bufLen C.int) C.int { + f := getFid(fid) + if f == nil { + setError(&errString{"invalid fid"}) + return -1 + } + + goBuf := unsafe.Slice((*byte)(unsafe.Pointer(buf)), int(bufLen)) + n, err := f.reader.Read(goBuf) + if err != nil { + if err == io.EOF { + setError(nil) + return 0 + } + setError(err) + return -1 + } + + setError(nil) + return C.int(n) +} + +// ollie9p_close_fid closes an open file handle. +// +//export ollie9p_close_fid +func ollie9p_close_fid(fid C.int) { + f := getFid(fid) + if f == nil { + return + } + if f.reader != nil { + f.reader.Close() + } + freeFid(fid) +} + // Helper types type errString struct{ s string }