blobsets/src/blobsets/filestores.nim

163 lines
4.6 KiB
Nim

import ../blobsets
import tiger
import std/asyncfile, std/asyncdispatch, std/os
proc ingestFile*(store: BlobStore; path: string): Future[tuple[id: BlobId, size: BiggestInt]] {.async.} =
## Ingest a file and return blob metadata.
let
file = openAsync(path, fmRead)
fileSize = file.getFileSize
defer:
close file
let stream = store.openIngestStream(fileSize, dataBlob)
if fileSize > 0:
var buf = newString(min(8 shl 10, fileSize))
while true:
let n = await file.readBuffer(buf[0].addr, buf.len)
if n == 0: break
await stream.ingest(buf[0].addr, n)
return await finish stream
type
FsBlobStream = ref FsBlobStreamObj
FsBlobStreamObj = object of BlobStreamObj
path: string
file: AsyncFile
FsIngestStream = ref FsIngestStreamObj
FsIngestStreamObj = object of IngestStreamObj
ctx: TigerState
leaves: seq[BlobId]
path: string
file: AsyncFile
pos, nodeOffset: BiggestInt
FileStore* = ref FileStoreObj
## A store that writes nodes and leafs as files.
FileStoreObj = object of BlobStoreObj
root, buf: string
proc fsBlobClose(s: BlobStream) =
var s = FsBlobStream(s)
close s.file
proc fsBlobSize(s: BlobStream): BiggestInt =
var s = FsBlobStream(s)
s.file.getFileSize.BiggestInt
proc setPosFs(s: BlobStream; pos: BiggestInt) =
var s = FsBlobStream(s)
s.file.setFilePos (int64)pos
proc getPosFs(s: BlobStream): BiggestInt =
var s = FsBlobStream(s)
(BiggestInt)s.file.getFilePos
proc fsBlobRead(s: BlobStream; buffer: pointer; len: Natural): Future[int] =
var s = FsBlobStream(s)
s.file.readBuffer(buffer, len)
proc fsOpenBlobStream(s: BlobStore; id: BlobId; size: BiggestInt; kind: BlobKind): BlobStream =
var fs = FileStore(s)
try:
let
path = fs.root / $kind / id.toBase32
file = openAsync(path, fmRead)
result = FsBlobStream(
closeImpl: fsBlobClose,
sizeImpl: fsBlobSize,
setPosImpl: setPosFs,
getPosImpl: getPosFs,
readImpl: fsBlobRead,
path: path, file: file,
)
except:
raise newException(KeyError, "blob not in file-system store")
proc fsFinish(s: IngestStream): Future[tuple[id: BlobId, size: BiggestInt]] =
var
s = FsIngestStream(s)
pair: tuple[id: BlobId, size: BiggestInt]
close s.file
if s.pos == 0 or s.pos mod blobLeafSize != 0:
s.leaves.add finish(s.ctx)
compressTree(s.leaves)
pair.id = s.leaves[0]
pair.size = s.pos
let finalPath = s.path.parentDir / pair.id.toBase32
if fileExists finalPath:
removeFile s.path
else:
moveFile(s.path, finalPath)
result = newFuture[tuple[id: BlobId, size: BiggestInt]]()
complete result, pair
proc appendLeaf(s: FsIngestStream) =
s.leaves.add(finish s.ctx)
init s.ctx
s.ctx.update [0'u8]
proc fsIngest(s: IngestStream; data: pointer; size: Natural): Future[void] =
let
s = FsIngestStream(s)
buf = cast[ptr UncheckedArray[byte]](data)
result = s.file.writeBuffer(data, size)
var dataOff: int
let leafOff = s.pos.int mod blobLeafSize
if leafOff != 0:
let leafFill = min(blobLeafSize - leafOff, size)
s.ctx.update(buf[0].addr, leafFill)
dataOff.inc leafFill
if leafFill < size:
appendLeaf s
while dataOff+blobLeafSize <= size:
s.ctx.update(buf[dataOff].addr, blobLeafSize)
dataOff.inc blobLeafSize
appendLeaf s
if dataOff != size:
s.ctx.update(buf[dataOff].addr, size - dataOff)
s.pos.inc size
proc fsOpenIngestStream(s: BlobStore; size: BiggestInt; kind: BlobKind): IngestStream =
var fs = FileStore(s)
let stream = FsIngestStream(
finishImpl: fsFinish,
ingestImpl: fsIngest,
path: fs.root / $kind / "ingest"
)
try: stream.file = openAsync(stream.path, fmWrite)
except: raise newException(OSError,
"failed to create ingest stream at '" & stream.path & "' " & getCurrentExceptionMsg())
if size > 0:
stream.file.setFileSize(size)
stream.leaves = newSeqOfCap[BlobId](leafCount size)
else:
stream.leaves = newSeq[BlobId]()
init stream.ctx
stream.ctx.update [0'u8]
stream
proc fsContains(s: BlobStore; id: BlobId; kind: BlobKind): Future[bool] =
var fs = FileStore(s)
result = newFuture[bool]("blobsets.filestores.fsContains")
let path = fs.root / $kind / id.toBase32
try:
close(openAsync(path, fmRead))
result.complete(true)
except:
result.complete(false)
proc newFileStore*(root: string): BlobStore =
## Create a new store object backed by a file-system.
try:
createDir(root / $dataBlob)
createDir(root / $metaBlob)
except: discard
FileStore(
containsImpl: fsContains,
openBlobStreamImpl: fsOpenBlobStream,
openIngestStreamImpl: fsOpenIngestStream,
root: root,
buf: "",
)