2022-05-23 23:24:15 -06:00
|
|
|
## Nim-Dagger
|
2022-03-17 07:56:46 -06:00
|
|
|
## Copyright (c) 2022 Status Research & Development GmbH
|
|
|
|
## Licensed under either of
|
|
|
|
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
|
|
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
|
|
## at your option.
|
|
|
|
## This file may not be copied, modified, or distributed except according to
|
|
|
|
## those terms.
|
|
|
|
|
2022-07-28 03:39:17 +03:00
|
|
|
import std/options
|
|
|
|
|
2022-03-21 12:09:59 -06:00
|
|
|
import pkg/upraises
|
|
|
|
|
|
|
|
push: {.upraises: [].}
|
2022-03-17 07:56:46 -06:00
|
|
|
|
|
|
|
import pkg/chronos
|
|
|
|
import pkg/chronicles
|
|
|
|
import pkg/stew/ptrops
|
|
|
|
|
2022-03-21 12:09:59 -06:00
|
|
|
import ../stores
|
|
|
|
import ../manifest
|
|
|
|
import ../blocktype
|
2023-10-12 15:56:10 +02:00
|
|
|
import ../utils
|
2022-03-21 12:09:59 -06:00
|
|
|
|
|
|
|
import ./seekablestream
|
2022-03-17 07:56:46 -06:00
|
|
|
|
2022-03-21 12:09:59 -06:00
|
|
|
export stores, blocktype, manifest, chronos
|
2022-03-17 07:56:46 -06:00
|
|
|
|
|
|
|
logScope:
|
2022-11-15 09:46:21 -06:00
|
|
|
topics = "codex storestream"
|
2022-03-17 07:56:46 -06:00
|
|
|
|
2023-06-22 12:01:21 -06:00
|
|
|
const
|
|
|
|
StoreStreamTrackerName* = "StoreStream"
|
|
|
|
|
2022-03-17 07:56:46 -06:00
|
|
|
type
|
2023-10-12 15:56:10 +02:00
|
|
|
StoreStream* = ref object of LPStream
|
2022-08-24 15:15:59 +03:00
|
|
|
store*: BlockStore # Store where to lookup block contents
|
|
|
|
manifest*: Manifest # List of block CIDs
|
|
|
|
pad*: bool # Pad last block to manifest.blockSize?
|
2023-10-12 15:56:10 +02:00
|
|
|
iter*: AsyncIter[?!Block]
|
|
|
|
lastBlock: Block
|
|
|
|
lastIndex: int
|
|
|
|
offset: int
|
2022-03-17 07:56:46 -06:00
|
|
|
|
2023-06-22 12:01:21 -06:00
|
|
|
method initStream*(s: StoreStream) =
|
|
|
|
if s.objName.len == 0:
|
|
|
|
s.objName = StoreStreamTrackerName
|
|
|
|
|
2023-10-12 15:56:10 +02:00
|
|
|
procCall LPStream(s).initStream()
|
2023-06-22 12:01:21 -06:00
|
|
|
|
2022-03-29 20:43:35 -06:00
|
|
|
proc new*(
|
2023-06-22 08:11:18 -07:00
|
|
|
T: type StoreStream,
|
|
|
|
store: BlockStore,
|
|
|
|
manifest: Manifest,
|
|
|
|
pad = true
|
|
|
|
): StoreStream =
|
|
|
|
## Create a new StoreStream instance for a given store and manifest
|
2023-08-22 08:35:16 +02:00
|
|
|
##
|
2023-06-22 08:11:18 -07:00
|
|
|
result = StoreStream(
|
2022-03-17 07:56:46 -06:00
|
|
|
store: store,
|
|
|
|
manifest: manifest,
|
2022-08-24 15:15:59 +03:00
|
|
|
pad: pad,
|
2023-10-12 15:56:10 +02:00
|
|
|
lastIndex: -1,
|
2022-08-24 15:15:59 +03:00
|
|
|
offset: 0)
|
2022-03-17 07:56:46 -06:00
|
|
|
|
|
|
|
result.initStream()
|
|
|
|
|
2022-06-14 09:19:35 -06:00
|
|
|
method `size`*(self: StoreStream): int =
|
2023-07-06 16:23:27 -07:00
|
|
|
bytes(self.manifest, self.pad).int
|
2022-03-21 12:09:59 -06:00
|
|
|
|
2022-06-14 09:19:35 -06:00
|
|
|
proc `size=`*(self: StoreStream, size: int)
|
|
|
|
{.error: "Setting the size is forbidden".} =
|
|
|
|
discard
|
|
|
|
|
2022-07-28 03:39:17 +03:00
|
|
|
method atEof*(self: StoreStream): bool =
|
|
|
|
self.offset >= self.size
|
|
|
|
|
2022-03-17 07:56:46 -06:00
|
|
|
method readOnce*(
|
2023-06-22 08:11:18 -07:00
|
|
|
self: StoreStream,
|
|
|
|
pbytes: pointer,
|
|
|
|
nbytes: int
|
|
|
|
): Future[int] {.async.} =
|
2022-07-28 03:39:17 +03:00
|
|
|
## Read `nbytes` from current position in the StoreStream into output buffer pointed by `pbytes`.
|
|
|
|
## Return how many bytes were actually read before EOF was encountered.
|
|
|
|
## Raise exception if we are already at EOF.
|
2023-08-22 08:35:16 +02:00
|
|
|
##
|
2022-03-17 07:56:46 -06:00
|
|
|
|
2023-10-12 15:56:10 +02:00
|
|
|
trace "Reading from manifest", cid = self.manifest.cid.get(), blocks = self.manifest.blocksCount
|
2022-03-17 07:56:46 -06:00
|
|
|
if self.atEof:
|
|
|
|
raise newLPStreamEOFError()
|
2023-10-12 15:56:10 +02:00
|
|
|
|
|
|
|
# Initialize a block iterator
|
|
|
|
if self.lastIndex < 0:
|
|
|
|
without iter =? await self.store.getBlocks(self.manifest.treeCid, self.manifest.blocksCount, self.manifest.treeRoot), err:
|
|
|
|
raise newLPStreamReadError(err)
|
|
|
|
self.iter = iter
|
2022-03-17 07:56:46 -06:00
|
|
|
|
2022-07-28 03:39:17 +03:00
|
|
|
var read = 0 # Bytes read so far, and thus write offset in the outbuf
|
2022-03-29 20:43:35 -06:00
|
|
|
while read < nbytes and not self.atEof:
|
2023-10-12 15:56:10 +02:00
|
|
|
if self.offset >= (self.lastIndex + 1) * self.manifest.blockSize.int:
|
|
|
|
if not self.iter.finished:
|
|
|
|
without lastBlock =? await self.iter.next(), err:
|
|
|
|
raise newLPStreamReadError(err)
|
|
|
|
self.lastBlock = lastBlock
|
|
|
|
inc self.lastIndex
|
|
|
|
else:
|
|
|
|
raise newLPStreamReadError(newException(CodexError, "Block iterator finished prematurely"))
|
2022-07-28 03:39:17 +03:00
|
|
|
# Compute how many bytes to read from this block
|
2022-03-17 07:56:46 -06:00
|
|
|
let
|
2023-07-06 16:23:27 -07:00
|
|
|
blockOffset = self.offset mod self.manifest.blockSize.int
|
|
|
|
readBytes = min([self.size - self.offset,
|
|
|
|
nbytes - read,
|
|
|
|
self.manifest.blockSize.int - blockOffset])
|
2022-07-28 03:39:17 +03:00
|
|
|
# Copy `readBytes` bytes starting at `blockOffset` from the block into the outbuf
|
2023-10-12 15:56:10 +02:00
|
|
|
if self.lastBlock.isEmpty:
|
2022-08-24 15:15:59 +03:00
|
|
|
zeroMem(pbytes.offset(read), readBytes)
|
|
|
|
else:
|
2023-10-12 15:56:10 +02:00
|
|
|
copyMem(pbytes.offset(read), self.lastBlock.data[blockOffset].addr, readBytes)
|
2022-05-23 23:24:15 -06:00
|
|
|
|
2022-07-28 03:39:17 +03:00
|
|
|
# Update current positions in the stream and outbuf
|
2022-03-17 07:56:46 -06:00
|
|
|
self.offset += readBytes
|
|
|
|
read += readBytes
|
|
|
|
|
|
|
|
return read
|
|
|
|
|
|
|
|
method closeImpl*(self: StoreStream) {.async.} =
|
2022-08-24 15:15:59 +03:00
|
|
|
trace "Closing StoreStream"
|
|
|
|
self.offset = self.size # set Eof
|
2022-03-17 07:56:46 -06:00
|
|
|
await procCall LPStream(self).closeImpl()
|