CIDv0 support
This commit is contained in:
parent
3d0bbca3b4
commit
7203eb2a65
64
README.md
64
README.md
|
@ -1,60 +1,8 @@
|
||||||
# WARNING
|
An IPFS library. No support for Protocol Buffers, which means no support
|
||||||
|
for the current IPFS file-system.
|
||||||
|
|
||||||
Contains my own contrived standards and formats that will break often.
|
Upstream development on CBOR file-system structures has predictably stalled
|
||||||
|
over discussion of features such as non-ASCII, non-unicode file-names
|
||||||
|
and extended attributes.
|
||||||
|
|
||||||
## ipldrepl
|
Current plan is to fork, postponed indefinitly.
|
||||||
|
|
||||||
A Lisp REPL utility for storing files and directories in IPLD.
|
|
||||||
|
|
||||||
### Functions
|
|
||||||
|
|
||||||
#### `(apply <function> <list>)`
|
|
||||||
|
|
||||||
Standard Lisp `apply` function, apply a list as arguments to a function.
|
|
||||||
|
|
||||||
#### `(cbor <cid>)`
|
|
||||||
|
|
||||||
Return CBOR encoding of UnixFS node as a diagnostic string.
|
|
||||||
Provided for illustrating canonicalized CBOR encoding.
|
|
||||||
|
|
||||||
#### `(cons <head> <tail>)`
|
|
||||||
|
|
||||||
Standard Lisp `cons` function, prepend to a list.
|
|
||||||
|
|
||||||
#### `(copy <cid> <from> <to>)`
|
|
||||||
|
|
||||||
Duplicate a directory entry.
|
|
||||||
|
|
||||||
#### `(define <symbol> <value>)`
|
|
||||||
|
|
||||||
Bind a value to a symbol. Returns value.
|
|
||||||
|
|
||||||
#### `(glob <glob-string> <...>)`
|
|
||||||
|
|
||||||
Return a list of paths matching a Unix-style glob string.
|
|
||||||
|
|
||||||
#### `(ingest <path> <...>)`
|
|
||||||
|
|
||||||
Ingest a list of paths to the store, returning a CID to a directory with
|
|
||||||
the contents each path under the trailing name of the path.
|
|
||||||
|
|
||||||
#### `(map <function> <list>)`
|
|
||||||
|
|
||||||
Standard Lisp `map`, apply a function to each member of a list.
|
|
||||||
|
|
||||||
#### `(merge <cid> <...>)`
|
|
||||||
|
|
||||||
Merge a list of root directories represented by CIDs into a new root.
|
|
||||||
Members of the root are not merged recursively.
|
|
||||||
|
|
||||||
#### `(path <string>)`
|
|
||||||
|
|
||||||
Convert a string to a path, if the path is valid and present.
|
|
||||||
|
|
||||||
#### `(root <string> <cid>)`
|
|
||||||
|
|
||||||
Create a new root contaning the giving CID at the given name.
|
|
||||||
|
|
||||||
#### `(walk <cid> <string>)`
|
|
||||||
|
|
||||||
Walk a path down one CID to another.
|
|
||||||
|
|
1025
blake2b-kat.txt
1025
blake2b-kat.txt
File diff suppressed because it is too large
Load Diff
|
@ -2,11 +2,12 @@
|
||||||
|
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
author = "Emery Hemingway"
|
author = "Emery Hemingway"
|
||||||
description = "IPLD library"
|
description = "InterPlanetary Linked Data library"
|
||||||
license = "GPLv3"
|
license = "GPLv3"
|
||||||
|
srcDir = "src"
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
requires "nim >= 0.17.3", "nimSHA2", "base58", "cbor >= 0.2.0"
|
requires "nim >= 0.18.1", "nimSHA2", "base58", "cbor >= 0.2.0"
|
||||||
|
|
||||||
bin = @["ipld/ipldrepl","ipld/ipldcat"]
|
bin = @["src/ipld/ipldrepl","src/ipld/ipldcat"]
|
||||||
|
|
170
ipld/ipld.nim
170
ipld/ipld.nim
|
@ -1,170 +0,0 @@
|
||||||
import nimSHA2, streams, multiformats, base58.bitcoin, cbor, hex, hashes, blake2
|
|
||||||
|
|
||||||
const MaxBlockSize* = 1 shl 18
|
|
||||||
## Maximum supported block size.
|
|
||||||
|
|
||||||
type Cid* = object
|
|
||||||
## Content IDentifier, used to identify blocks.
|
|
||||||
digest*: seq[uint8]
|
|
||||||
hash*: MulticodecTag
|
|
||||||
codec*: MulticodecTag
|
|
||||||
ver*: int
|
|
||||||
|
|
||||||
proc initCid*(): Cid =
|
|
||||||
## Initialize an invalid CID.
|
|
||||||
Cid(hash: MulticodecTag.Invalid, codec: MulticodecTag.Invalid)
|
|
||||||
|
|
||||||
proc isValid*(x: Cid): bool =
|
|
||||||
## Check that a CID has been properly initialized.
|
|
||||||
x.hash != MulticodecTag.Invalid
|
|
||||||
|
|
||||||
proc `==`*(x, y: Cid): bool =
|
|
||||||
## Compare two CIDs. Must be of the same type and
|
|
||||||
## use the same hash algorithm.
|
|
||||||
result =
|
|
||||||
x.ver == y.ver and
|
|
||||||
x.codec == y.codec and
|
|
||||||
x.hash == y.hash and
|
|
||||||
x.digest == y.digest
|
|
||||||
|
|
||||||
proc hash*(cid: Cid): Hash =
|
|
||||||
## Reduce a CID into an integer for use in tables.
|
|
||||||
result = hash cid.digest
|
|
||||||
result = result !& cid.ver !& cid.codec.int !& cid.hash.int
|
|
||||||
result = !$result
|
|
||||||
|
|
||||||
proc isRaw*(cid: Cid): bool =
|
|
||||||
## Test if a CID represents a raw block.
|
|
||||||
cid.codec == MulticodecTag.Raw
|
|
||||||
|
|
||||||
proc isDagCbor*(cid: Cid): bool =
|
|
||||||
## Test if a CID represents CBOR encoded data.
|
|
||||||
cid.codec == MulticodecTag.DagCbor
|
|
||||||
|
|
||||||
proc toBin(cid: Cid): string =
|
|
||||||
let s = newStringStream()
|
|
||||||
s.writeUvarint cid.ver
|
|
||||||
s.writeUvarint cid.codec.int
|
|
||||||
s.writeUvarint cid.hash.int
|
|
||||||
s.writeUvarint cid.digest.len
|
|
||||||
for e in cid.digest:
|
|
||||||
s.write e
|
|
||||||
s.setPosition 0
|
|
||||||
result = s.readAll
|
|
||||||
close s
|
|
||||||
|
|
||||||
proc toRaw*(cid: Cid): string =
|
|
||||||
## Return CID encoded in binary.
|
|
||||||
MultibaseTag.Identity.char & cid.toBIn
|
|
||||||
|
|
||||||
proc newCborBytes*(cid: Cid): CborNode = newCborBytes cid.toRaw
|
|
||||||
## Generate a CBOR representation of a CID.
|
|
||||||
|
|
||||||
proc toHex*(cid: Cid): string =
|
|
||||||
## Return CID encoded in hexidecimal.
|
|
||||||
MultibaseTag.Base16.char & hex.encode(cid.toBin)
|
|
||||||
|
|
||||||
proc toBase58*(cid: Cid): string =
|
|
||||||
## Return CID encoded in base 58.
|
|
||||||
MultibaseTag.Base58btc.char & bitcoin.encode(cid.toBin)
|
|
||||||
|
|
||||||
proc `$`*(cid: Cid): string = cid.toBase58
|
|
||||||
## Return CID in base 58, the default textual encoding.
|
|
||||||
|
|
||||||
proc parseCid*(s: string): Cid =
|
|
||||||
## Detect CID encoding and parse from a string.
|
|
||||||
if unlikely(s.len < (1+1+1+1)):
|
|
||||||
raise newException(ValueError, "too short to be a valid CID")
|
|
||||||
var
|
|
||||||
raw: string
|
|
||||||
off: int
|
|
||||||
codec, hash: int
|
|
||||||
digestLen: int
|
|
||||||
case s[0].MultibaseTag
|
|
||||||
of MultibaseTag.Identity:
|
|
||||||
raw = s
|
|
||||||
off = 1
|
|
||||||
of MultibaseTag.Base16, MultibaseTag.InconsistentBase16:
|
|
||||||
raw = hex.decode(s[1..s.high])
|
|
||||||
if unlikely(raw.isNil):
|
|
||||||
raise newException(ValueError, "not a CID")
|
|
||||||
of MultibaseTag.Base58btc:
|
|
||||||
raw = bitcoin.decode(s[1..s.high])
|
|
||||||
else:
|
|
||||||
raise newException(ValueError, "unknown multibase encoding tag")
|
|
||||||
off.inc parseUvarint(raw, result.ver, off)
|
|
||||||
off.inc parseUvarint(raw, codec, off)
|
|
||||||
off.inc parseUvarint(raw, hash, off)
|
|
||||||
off.inc parseUvarint(raw, digestLen, off)
|
|
||||||
if unlikely(off + digestLen != raw.len):
|
|
||||||
raise newException(ValueError, "invalid multihash length")
|
|
||||||
result.digest = newSeq[uint8](digestLen)
|
|
||||||
for i in 0..<digestLen:
|
|
||||||
result.digest[i] = (uint8)raw[i+off]
|
|
||||||
result.hash = hash.MulticodecTag
|
|
||||||
result.codec = codec.MulticodecTag
|
|
||||||
|
|
||||||
proc CidSha256*(data: string; codec = MulticodecTag.Raw): Cid =
|
|
||||||
## Generate a CID for a string of data using the SHA 256 hash algorithm.
|
|
||||||
let sha = computeSHA256(data)
|
|
||||||
result.digest = newSeq[uint8](32)
|
|
||||||
for i in 0..31:
|
|
||||||
result.digest[i] = (uint8)sha[i]
|
|
||||||
result.hash = MulticodecTag.Sha2_256
|
|
||||||
result.codec = codec
|
|
||||||
result.ver = 1
|
|
||||||
|
|
||||||
proc CidBlake2b256*(data: string; codec = MulticodecTag.Raw): Cid =
|
|
||||||
## Generate a CID for a string of data using the BLAKE2b hash algorithm.
|
|
||||||
var b: Blake2b
|
|
||||||
blake2b_init(b, 32, nil, 0)
|
|
||||||
blake2b_update(b, data, data.len)
|
|
||||||
|
|
||||||
result.digest = blake2b_final(b)
|
|
||||||
result.hash = MulticodecTag.Blake2b_256
|
|
||||||
result.codec = codec
|
|
||||||
result.ver = 1
|
|
||||||
|
|
||||||
proc verify*(cid: Cid; data: string): bool =
|
|
||||||
## Verify that a string of data corresponds to a CID.
|
|
||||||
case cid.hash
|
|
||||||
of MulticodecTag.Sha2_256:
|
|
||||||
let sha = computeSHA256(data)
|
|
||||||
for i in 0..31:
|
|
||||||
if cid.digest[i] != (uint8)sha[i]:
|
|
||||||
return false
|
|
||||||
result = true
|
|
||||||
of MulticodecTag.Blake2b_256:
|
|
||||||
var b: Blake2b
|
|
||||||
blake2b_init(b, 32, nil, 0)
|
|
||||||
blake2b_update(b, data, data.len)
|
|
||||||
let digest = blake2b_final(b)
|
|
||||||
result = (cid.digest == digest)
|
|
||||||
else:
|
|
||||||
raise newException(ValueError, "unknown hash type " & $cid.hash)
|
|
||||||
|
|
||||||
iterator simpleChunks*(s: Stream; size = 256 * 1024): string =
|
|
||||||
## Iterator that breaks a stream into simple chunks.
|
|
||||||
while not s.atEnd:
|
|
||||||
yield s.readStr size
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
import times
|
|
||||||
|
|
||||||
let data = newString MaxBlockSize
|
|
||||||
block sha256:
|
|
||||||
var i = 0
|
|
||||||
let t0 = cpuTime()
|
|
||||||
while i < 100:
|
|
||||||
discard CidSha256 data
|
|
||||||
inc i
|
|
||||||
let d = cpuTime() - t0
|
|
||||||
echo "SHA256 ticks: ", d
|
|
||||||
block blake2b:
|
|
||||||
var i = 0
|
|
||||||
let t0 = cpuTime()
|
|
||||||
while i < 100:
|
|
||||||
discard CidBlake2b256 data
|
|
||||||
inc i
|
|
||||||
let d = cpuTime() - t0
|
|
||||||
echo "BLAKE2b ticks: ", d
|
|
235
src/ipld.nim
Normal file
235
src/ipld.nim
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
import std/hashes, std/streams, std/strutils
|
||||||
|
|
||||||
|
import nimSHA2, ./ipld/multiformats, base58/bitcoin, cbor, ./ipld/priv/hex, ./ipld/priv/blake2
|
||||||
|
|
||||||
|
const MaxBlockSize* = 1 shl 18
|
||||||
|
## Maximum supported block size.
|
||||||
|
|
||||||
|
type
|
||||||
|
CidVersion* = enum CIDv0, CIDv1
|
||||||
|
|
||||||
|
Cid* = object
|
||||||
|
## Content IDentifier, used to identify blocks.
|
||||||
|
case kind*: CidVersion
|
||||||
|
of CIDv0:
|
||||||
|
sha256: array[32, uint8]
|
||||||
|
of CIDv1:
|
||||||
|
digest*: seq[uint8] # this is stupid, make it a fixed size
|
||||||
|
hash*: MulticodecTag
|
||||||
|
codec*: MulticodecTag
|
||||||
|
ver*: int
|
||||||
|
|
||||||
|
proc initCid*(): Cid =
|
||||||
|
## Initialize an invalid CID.
|
||||||
|
Cid(kind: CIDv1, hash: MulticodecTag.Invalid, codec: MulticodecTag.Invalid)
|
||||||
|
|
||||||
|
proc isValid*(x: Cid): bool =
|
||||||
|
## Check that a CID has been properly initialized.
|
||||||
|
case x.kind
|
||||||
|
of CIDv0: true # whatever
|
||||||
|
of CIDv1: x.hash != MulticodecTag.Invalid
|
||||||
|
|
||||||
|
proc `==`*(x, y: Cid): bool =
|
||||||
|
## Compare two CIDs. Must be of the same type and
|
||||||
|
## use the same hash algorithm.
|
||||||
|
if x.kind == y.kind:
|
||||||
|
case x.kind
|
||||||
|
of CIDv0:
|
||||||
|
result = (x.sha256 == y.sha256)
|
||||||
|
of CIDv1:
|
||||||
|
result = (
|
||||||
|
x.ver == y.ver and
|
||||||
|
x.codec == y.codec and
|
||||||
|
x.hash == y.hash and
|
||||||
|
x.digest == y.digest)
|
||||||
|
|
||||||
|
proc hash*(cid: Cid): Hash =
|
||||||
|
## Reduce a CID into an integer for use in tables.
|
||||||
|
case cid.kind
|
||||||
|
of CIDv0:
|
||||||
|
result = hash cid.sha256
|
||||||
|
of CIDv1:
|
||||||
|
result = hash cid.digest
|
||||||
|
result = result !& cid.ver !& cid.codec.int !& cid.hash.int
|
||||||
|
result = !$result
|
||||||
|
|
||||||
|
proc isRaw*(cid: Cid): bool =
|
||||||
|
## Test if a CID represents a raw block.
|
||||||
|
cid.kind == CIDv1 and cid.codec == MulticodecTag.Raw
|
||||||
|
|
||||||
|
proc isDag*(cid: Cid): bool =
|
||||||
|
## Test if a CID represents protobuf or CBOR encoded data.
|
||||||
|
cid.kind == CIDv0 or cid.codec in {MulticodecTag.DagPb, MulticodecTag.DagCbor}
|
||||||
|
|
||||||
|
proc isDagPb*(cid: Cid): bool =
|
||||||
|
## Test if a CID represents protobuf encoded data.
|
||||||
|
cid.kind == CIDv0 or cid.codec == MulticodecTag.DagPb
|
||||||
|
|
||||||
|
proc isDagCbor*(cid: Cid): bool =
|
||||||
|
## Test if a CID represents CBOR encoded data.
|
||||||
|
cid.kind == CIDv1 and cid.codec == MulticodecTag.DagCbor
|
||||||
|
|
||||||
|
proc toBin(cid: Cid): string =
|
||||||
|
case cid.kind
|
||||||
|
of CIDv0:
|
||||||
|
result = newString(34)
|
||||||
|
result[0] = 0x12.char
|
||||||
|
result[1] = 0x20.char
|
||||||
|
var sha = cid.sha256
|
||||||
|
copyMem(result[2].addr, sha[0].addr, 32)
|
||||||
|
of CIDv1:
|
||||||
|
let s = newStringStream()
|
||||||
|
s.writeUvarint cid.ver
|
||||||
|
s.writeUvarint cid.codec.int
|
||||||
|
s.writeUvarint cid.hash.int
|
||||||
|
s.writeUvarint cid.digest.len
|
||||||
|
for e in cid.digest:
|
||||||
|
s.write e
|
||||||
|
s.setPosition 0
|
||||||
|
result = s.readAll
|
||||||
|
close s
|
||||||
|
|
||||||
|
proc toRaw*(cid: Cid): string =
|
||||||
|
## Return CID encoded in binary.
|
||||||
|
case cid.kind
|
||||||
|
of CIDv0:
|
||||||
|
cid.toBin
|
||||||
|
of CIDv1:
|
||||||
|
MultibaseTag.Identity.char & cid.toBIn
|
||||||
|
|
||||||
|
proc newCborBytes*(cid: Cid): CborNode = newCborBytes cid.toRaw
|
||||||
|
## Generate a CBOR representation of a CID.
|
||||||
|
|
||||||
|
proc toHex*(cid: Cid): string =
|
||||||
|
## Return CID encoded in hexidecimal.
|
||||||
|
assert(isValid cid)
|
||||||
|
MultibaseTag.Base16.char & hex.encode(cid.toBin)
|
||||||
|
|
||||||
|
proc toBase58*(cid: Cid): string =
|
||||||
|
## Return CID encoded in base 58.
|
||||||
|
assert(isValid cid)
|
||||||
|
case cid.kind
|
||||||
|
of CIDv0:
|
||||||
|
bitcoin.encode(cid.toBin)
|
||||||
|
of CIDv1:
|
||||||
|
MultibaseTag.Base58btc.char & bitcoin.encode(cid.toBin)
|
||||||
|
|
||||||
|
proc `$`*(cid: Cid): string =
|
||||||
|
## Return CID in base 58, the default textual encoding.
|
||||||
|
cid.toBase58
|
||||||
|
|
||||||
|
proc parseCid*(s: string): Cid =
|
||||||
|
## Detect CID encoding and parse from a string.
|
||||||
|
if unlikely(s.len < (1+1+1+1)):
|
||||||
|
raise newException(ValueError, "too short to be a valid CID")
|
||||||
|
var
|
||||||
|
raw: string
|
||||||
|
off: int
|
||||||
|
codec, hash: int
|
||||||
|
digestLen: int
|
||||||
|
if s.len == 46 and s.startsWith "Qm":
|
||||||
|
var data = bitcoin.decode(s)
|
||||||
|
if data.len == 34 and data.startsWith "\x12\x20":
|
||||||
|
result.kind = CIDv0
|
||||||
|
copyMem(result.sha256[0].addr, data[2].addr, 32)
|
||||||
|
else:
|
||||||
|
raise newException(ValueError, "invalid CIDv0")
|
||||||
|
else:
|
||||||
|
case s[0].MultibaseTag
|
||||||
|
of MultibaseTag.Identity:
|
||||||
|
raw = s
|
||||||
|
off = 1
|
||||||
|
of MultibaseTag.Base16, MultibaseTag.InconsistentBase16:
|
||||||
|
raw = hex.decode(s[1..s.high])
|
||||||
|
if unlikely(raw.isNil):
|
||||||
|
raise newException(ValueError, "not a CID")
|
||||||
|
of MultibaseTag.Base58btc:
|
||||||
|
raw = bitcoin.decode(s[1..s.high])
|
||||||
|
else:
|
||||||
|
raise newException(ValueError, "unknown multibase encoding tag")
|
||||||
|
off.inc parseUvarint(raw, result.ver, off)
|
||||||
|
off.inc parseUvarint(raw, codec, off)
|
||||||
|
off.inc parseUvarint(raw, hash, off)
|
||||||
|
off.inc parseUvarint(raw, digestLen, off)
|
||||||
|
if unlikely(off + digestLen != raw.len):
|
||||||
|
raise newException(ValueError, "invalid multihash length")
|
||||||
|
result.kind = CIDv0
|
||||||
|
result.digest = newSeq[uint8](digestLen)
|
||||||
|
for i in 0..<digestLen:
|
||||||
|
result.digest[i] = (uint8)raw[i+off]
|
||||||
|
result.hash = hash.MulticodecTag
|
||||||
|
result.codec = codec.MulticodecTag
|
||||||
|
|
||||||
|
proc CidSha256*(data: string; codec = MulticodecTag.Raw): Cid =
|
||||||
|
## Generate a CID for a string of data using the SHA 256 hash algorithm.
|
||||||
|
result.kind = CIDv1
|
||||||
|
let sha = computeSHA256(data)
|
||||||
|
result.digest = newSeq[uint8](32)
|
||||||
|
for i in 0..31:
|
||||||
|
result.digest[i] = (uint8)sha[i]
|
||||||
|
result.hash = MulticodecTag.Sha2_256
|
||||||
|
result.codec = codec
|
||||||
|
result.ver = 1
|
||||||
|
|
||||||
|
proc CidBlake2b256*(data: string; codec = MulticodecTag.Raw): Cid =
|
||||||
|
## Generate a CID for a string of data using the BLAKE2b hash algorithm.
|
||||||
|
result.kind = CIDv1
|
||||||
|
var b: Blake2b
|
||||||
|
blake2b_init(b, 32, nil, 0)
|
||||||
|
blake2b_update(b, data, data.len)
|
||||||
|
result.digest = blake2b_final(b)
|
||||||
|
result.hash = MulticodecTag.Blake2b_256
|
||||||
|
result.codec = codec
|
||||||
|
result.ver = 1
|
||||||
|
|
||||||
|
proc verify*(cid: Cid; data: string): bool =
|
||||||
|
## Verify that a string of data corresponds to a CID.
|
||||||
|
case cid.kind
|
||||||
|
of CIDv0:
|
||||||
|
let sha = computeSHA256(data)
|
||||||
|
for i in 0..31:
|
||||||
|
if cid.sha256[i] != (uint8)sha[i]:
|
||||||
|
return false
|
||||||
|
result = true
|
||||||
|
of CIDv1:
|
||||||
|
case cid.hash
|
||||||
|
of MulticodecTag.Sha2_256:
|
||||||
|
let sha = computeSHA256(data)
|
||||||
|
for i in 0..31:
|
||||||
|
if cid.digest[i] != (uint8)sha[i]:
|
||||||
|
return false
|
||||||
|
result = true
|
||||||
|
of MulticodecTag.Blake2b_256:
|
||||||
|
var b: Blake2b
|
||||||
|
blake2b_init(b, 32, nil, 0)
|
||||||
|
blake2b_update(b, data, data.len)
|
||||||
|
let digest = blake2b_final(b)
|
||||||
|
result = (cid.digest == digest)
|
||||||
|
else:
|
||||||
|
raise newException(ValueError, "unknown hash type " & $cid.hash)
|
||||||
|
|
||||||
|
iterator simpleChunks*(s: Stream; size = 256 * 1024): string =
|
||||||
|
## Iterator that breaks a stream into simple chunks.
|
||||||
|
while not s.atEnd:
|
||||||
|
yield s.readStr size
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import times
|
||||||
|
|
||||||
|
let data = newString MaxBlockSize
|
||||||
|
block sha256:
|
||||||
|
var i = 0
|
||||||
|
let t0 = cpuTime()
|
||||||
|
while i < 100:
|
||||||
|
discard CidSha256 data
|
||||||
|
inc i
|
||||||
|
let d = cpuTime() - t0
|
||||||
|
echo "SHA256 ticks: ", d
|
||||||
|
block blake2b:
|
||||||
|
var i = 0
|
||||||
|
let t0 = cpuTime()
|
||||||
|
while i < 100:
|
||||||
|
discard CidBlake2b256 data
|
||||||
|
inc i
|
||||||
|
let d = cpuTime() - t0
|
||||||
|
echo "BLAKE2b ticks: ", d
|
28
src/ipld/genode/ipldclient.h
Normal file
28
src/ipld/genode/ipldclient.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* \brief C++ base of IPLD client
|
||||||
|
* \author Emery Hemingway
|
||||||
|
* \date 2017-11-08
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <ipld_session/connection.h>
|
||||||
|
#include <base/heap.h>
|
||||||
|
|
||||||
|
struct IpldClientBase
|
||||||
|
{
|
||||||
|
Genode::Heap heap;
|
||||||
|
Genode::Allocator_avl tx_packet_alloc { &heap };
|
||||||
|
Ipld::Connection conn;
|
||||||
|
|
||||||
|
IpldClientBase(Genode::Env *env, char const *label, Genode::size_t tx_buf_size)
|
||||||
|
: heap(env->pd(), env->rm()),
|
||||||
|
conn(*env, tx_packet_alloc, label, tx_buf_size)
|
||||||
|
{ }
|
||||||
|
};
|
147
src/ipld/genode/ipldclient.nim
Normal file
147
src/ipld/genode/ipldclient.nim
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
#
|
||||||
|
# \brief IpldStore interface to the IPLD session
|
||||||
|
# \author Emery Hemingway
|
||||||
|
# \date 2017-11-04
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 Genode Labs GmbH
|
||||||
|
#
|
||||||
|
# This file is part of the Genode OS framework, which is distributed
|
||||||
|
# under the terms of the GNU Affero General Public License version 3.
|
||||||
|
#
|
||||||
|
|
||||||
|
when not defined(genode):
|
||||||
|
{.error: "Genode only IPLD client".}
|
||||||
|
|
||||||
|
import cbor, genode, tables, strutils
|
||||||
|
|
||||||
|
import ipld, ipld/multiformats, ipld/store, ipld/genode/ipldsession
|
||||||
|
|
||||||
|
const
|
||||||
|
currentPath = currentSourcePath.rsplit("/", 1)[0]
|
||||||
|
ipldClientH = currentPath & "/ipldclient.h"
|
||||||
|
{.passC: "-I" & currentPath.}
|
||||||
|
|
||||||
|
type
|
||||||
|
IpldClientBase {.importcpp, header: ipldClientH.} = object
|
||||||
|
IpldClientCpp = Constructible[IpldClientBase]
|
||||||
|
|
||||||
|
proc sigh_ack_avail(cpp: IpldClientCpp; sig: SignalContextCapability) {.
|
||||||
|
importcpp: "#->conn.channel().sigh_ack_avail(@)", tags: [RpcEffect].}
|
||||||
|
|
||||||
|
proc readyToSubmit(cpp: IpldClientCpp): bool {.
|
||||||
|
importcpp: "#->conn.source().ready_to_submit()".}
|
||||||
|
|
||||||
|
proc readyToAck(cpp: IpldClientCpp): bool {.
|
||||||
|
importcpp: "#->conn.source().ready_to_ack()".}
|
||||||
|
|
||||||
|
proc ackAvail(cpp: IpldClientCpp): bool {.
|
||||||
|
importcpp: "#->conn.source().ack_avail()".}
|
||||||
|
|
||||||
|
proc allocPacket(cpp: IpldClientCpp; size = MaxPacketSize): IpldPacket {.
|
||||||
|
importcpp: "#->conn.source().alloc_packet(@)".}
|
||||||
|
|
||||||
|
proc packetContent(cpp: IpldClientCpp; pkt: IpldPacket): pointer {.
|
||||||
|
importcpp: "#->conn.source().packet_content(@)".}
|
||||||
|
|
||||||
|
proc submitPacket(cpp: IpldClientCpp; pkt: IpldPacket; cid: cstring; op: IpldOpcode) {.
|
||||||
|
importcpp: "#->conn.source().submit_packet(Ipld::Packet(#, (char const *)#, #))".}
|
||||||
|
|
||||||
|
proc getAckedPacket(cpp: IpldClientCpp): IpldPacket {.
|
||||||
|
importcpp: "#->conn.source().get_acked_packet()".}
|
||||||
|
|
||||||
|
proc releasePacket(cpp: IpldClientCpp; pkt: IpldPacket) {.
|
||||||
|
importcpp: "#->conn.source().release_packet(@)".}
|
||||||
|
|
||||||
|
type
|
||||||
|
IpldClient* = ref IpldClientObj
|
||||||
|
IpldClientObj = object of IpldStoreObj
|
||||||
|
## IPLD session client
|
||||||
|
cpp: IpldClientCpp
|
||||||
|
|
||||||
|
proc icClose(s: IpldStore) =
|
||||||
|
var ic = IpldClient(s)
|
||||||
|
destruct ic.cpp
|
||||||
|
|
||||||
|
proc icPut(s: IpldStore; blk: string; hash: MulticodecTag): Cid =
|
||||||
|
## Put block to Ipld server, blocks for two packet round-trip.
|
||||||
|
let ic = IpldClient(s)
|
||||||
|
var blk = blk
|
||||||
|
var pktCid = initCid()
|
||||||
|
pktCid.hash = hash
|
||||||
|
assert(ic.cpp.readyToSubmit, "Ipld client packet queue congested")
|
||||||
|
var pkt = ic.cpp.allocPacket(blk.len)
|
||||||
|
let pktBuf = ic.cpp.packetContent pkt
|
||||||
|
defer: ic.cpp.releasePacket pkt
|
||||||
|
assert(not pktBuf.isNil, "allocated packet has nil content")
|
||||||
|
assert(pkt.size >= blk.len)
|
||||||
|
pkt.setLen blk.len
|
||||||
|
copyMem(pktBuf, blk[0].addr, blk.len)
|
||||||
|
assert(ic.cpp.readyToSubmit, "Ipld client packet queue congested")
|
||||||
|
ic.cpp.submitPacket(pkt, pktCid.toHex, PUT)
|
||||||
|
let ack = ic.cpp.getAckedPacket()
|
||||||
|
doAssert(ack.error == OK)
|
||||||
|
result = ack.cid()
|
||||||
|
assert(result.isValid, "server returned a packet with and invalid CID")
|
||||||
|
|
||||||
|
proc icGetBuffer(s: IpldStore; cid: Cid; buf: pointer; len: Natural): int =
|
||||||
|
## Get from Ipld server, blocks for packet round-trip.
|
||||||
|
let ic = IpldClient(s)
|
||||||
|
assert(ic.cpp.readyToSubmit, "Ipld client packet queue congested")
|
||||||
|
let pkt = ic.cpp.allocPacket len
|
||||||
|
ic.cpp.submitPacket(pkt, cid.toHex, GET)
|
||||||
|
let ack = ic.cpp.getAckedPacket
|
||||||
|
doAssert(ack.cid == cid)
|
||||||
|
if ack.error == OK:
|
||||||
|
let pktBuf = ic.cpp.packetContent ack
|
||||||
|
assert(not pktBuf.isNil, "ack packet has nil content")
|
||||||
|
assert(ack.len <= len)
|
||||||
|
assert(ack.len > 0)
|
||||||
|
result = ack.len
|
||||||
|
copyMem(buf, pktBuf, result)
|
||||||
|
if pkt.size > 0:
|
||||||
|
ic.cpp.releasePacket pkt
|
||||||
|
# free the original packet that was allocated
|
||||||
|
case ack.error:
|
||||||
|
of OK: discard
|
||||||
|
of MISSING:
|
||||||
|
raise cid.newMissingObject
|
||||||
|
else:
|
||||||
|
raise newException(SystemError, "Ipld packet error " & $ack.error)
|
||||||
|
|
||||||
|
proc icGet(s: IpldStore; cid: Cid; result: var string) =
|
||||||
|
## Get from Ipld server, blocks for packet round-trip.
|
||||||
|
let ic = IpldClient(s)
|
||||||
|
assert(ic.cpp.readyToSubmit, "Ipld client packet queue congested")
|
||||||
|
let pkt = ic.cpp.allocPacket()
|
||||||
|
defer: ic.cpp.releasePacket pkt
|
||||||
|
ic.cpp.submitPacket(pkt, cid.toHex, GET)
|
||||||
|
let ack = ic.cpp.getAckedPacket()
|
||||||
|
doAssert(ack.cid == cid)
|
||||||
|
case ack.error:
|
||||||
|
of OK:
|
||||||
|
let ackBuf = ic.cpp.packetContent ack
|
||||||
|
assert(not ackBuf.isNil)
|
||||||
|
assert(ack.len > 0)
|
||||||
|
result.setLen ack.len
|
||||||
|
copyMem(result[0].addr, ackBuf, result.len)
|
||||||
|
assert(cid.verify(result), "Ipld client packet failed verification")
|
||||||
|
of MISSING:
|
||||||
|
raise cid.newMissingObject
|
||||||
|
else:
|
||||||
|
raise newException(SystemError, "Ipld packet error " & $ack.error)
|
||||||
|
|
||||||
|
const
|
||||||
|
DefaultIpldBufferSize* = 1 shl 20
|
||||||
|
|
||||||
|
proc newIpldClient*(env: GenodeEnv; label = ""; bufferSize = DefaultIpldBufferSize): IpldClient =
|
||||||
|
## Blocks retrieved by `get` are not verified.
|
||||||
|
proc construct(cpp: IpldClientCpp; env: GenodeEnv; label: cstring; txBufSize: int) {.
|
||||||
|
importcpp.}
|
||||||
|
new result
|
||||||
|
construct(result.cpp, env, label, bufferSize)
|
||||||
|
result.closeImpl = icClose
|
||||||
|
result.putImpl = icPut
|
||||||
|
result.getBufferImpl = icGetBuffer
|
||||||
|
result.getImpl = icGet
|
56
src/ipld/genode/ipldserver.h
Normal file
56
src/ipld/genode/ipldserver.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* \brief IPLD C++ session component
|
||||||
|
* \author Emery Hemingway
|
||||||
|
* \date 2017-11-07
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _INCLUDE__NIM__IPLDSERVER_H_
|
||||||
|
#define _INCLUDE__NIM__IPLDSERVER_H_
|
||||||
|
|
||||||
|
#include <ipld_session/rpc_object.h>
|
||||||
|
#include <base/heap.h>
|
||||||
|
#include <base/attached_ram_dataspace.h>
|
||||||
|
|
||||||
|
struct Communication_buffer
|
||||||
|
{
|
||||||
|
Genode::Attached_ram_dataspace _tx_ds;
|
||||||
|
|
||||||
|
Communication_buffer(Genode::Pd_session &pd,
|
||||||
|
Genode::Region_map &rm,
|
||||||
|
Genode::size_t tx_buf_size)
|
||||||
|
: _tx_ds(pd, rm, tx_buf_size) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IpldSessionComponentBase : Communication_buffer,
|
||||||
|
Ipld::Session_rpc_object
|
||||||
|
{
|
||||||
|
static Genode::size_t tx_buf_size(char const *args)
|
||||||
|
{
|
||||||
|
Genode::size_t const buf_size = Genode::Arg_string::find_arg(
|
||||||
|
args, "tx_buf_size").aligned_size();
|
||||||
|
if (!buf_size)
|
||||||
|
throw Genode::Service_denied();
|
||||||
|
return buf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
IpldSessionComponentBase(Genode::Env *env, char const *args)
|
||||||
|
:
|
||||||
|
Communication_buffer(env->pd(), env->rm(), tx_buf_size(args)),
|
||||||
|
Session_rpc_object(env->rm(), env->ep().rpc_ep(), _tx_ds.cap())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void packetHandler(Genode::Signal_context_capability cap)
|
||||||
|
{
|
||||||
|
_tx.sigh_ready_to_ack(cap);
|
||||||
|
_tx.sigh_packet_avail(cap);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _INCLUDE__NIM__IPLDSERVER_H_ */
|
142
src/ipld/genode/ipldserver.nim
Normal file
142
src/ipld/genode/ipldserver.nim
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
#
|
||||||
|
# \brief IPLD server factory
|
||||||
|
# \author Emery Hemingway
|
||||||
|
# \date 2017-11-11
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 Genode Labs GmbH
|
||||||
|
#
|
||||||
|
# This file is part of the Genode OS framework, which is distributed
|
||||||
|
# under the terms of the GNU Affero General Public License version 3.
|
||||||
|
#
|
||||||
|
|
||||||
|
import std/strtabs, std/tables, std/xmltree, std/strutils
|
||||||
|
|
||||||
|
import cbor, genode, genode/signals, genode/servers, ipld, ipld/store, ipldsession
|
||||||
|
|
||||||
|
const
|
||||||
|
currentPath = currentSourcePath.rsplit("/", 1)[0]
|
||||||
|
ipldserverH = currentPath & "/ipldserver.h"
|
||||||
|
|
||||||
|
type
|
||||||
|
IpldSessionComponentBase {.importcpp, header: ipldserverH.} = object
|
||||||
|
SessionCpp = Constructible[IpldSessionComponentBase]
|
||||||
|
Session = ref object
|
||||||
|
cpp: SessionCpp
|
||||||
|
sig: SignalHandler
|
||||||
|
store: IpldStore
|
||||||
|
id: SessionId
|
||||||
|
label: string
|
||||||
|
|
||||||
|
proc processPacket(session: Session; pkt: var IpldPacket) =
|
||||||
|
proc packetContent(cpp: SessionCpp; pkt: IpldPacket): pointer {.
|
||||||
|
importcpp: "#->sink().packet_content(@)".}
|
||||||
|
let cid = pkt.cid
|
||||||
|
case pkt.operation
|
||||||
|
of PUT:
|
||||||
|
try:
|
||||||
|
var
|
||||||
|
pktBuf = session.cpp.packetContent pkt
|
||||||
|
heapBuf = newString pkt.len
|
||||||
|
copyMem(heapBuf[0].addr, pktBuf, heapBuf.len)
|
||||||
|
let putCid = session.store.put(heapBuf, cid.hash)
|
||||||
|
assert(putCid.isValid, "server packet returned invalid CID from put")
|
||||||
|
pkt.setCid putCid
|
||||||
|
except:
|
||||||
|
echo "unhandled PUT error ", getCurrentExceptionMsg()
|
||||||
|
pkt.setError ERROR
|
||||||
|
of GET:
|
||||||
|
try:
|
||||||
|
let
|
||||||
|
pktBuf = session.cpp.packetContent pkt
|
||||||
|
n = session.store.getBuffer(cid, pktBuf, pkt.size)
|
||||||
|
pkt.setLen n
|
||||||
|
except BufferTooSmall:
|
||||||
|
pkt.setError OVERSIZE
|
||||||
|
except MissingObject:
|
||||||
|
pkt.setError MISSING
|
||||||
|
except:
|
||||||
|
echo "unhandled GET error ", getCurrentExceptionMsg()
|
||||||
|
pkt.setError ERROR
|
||||||
|
else:
|
||||||
|
echo "invalid packet operation"
|
||||||
|
pkt.setError ERROR
|
||||||
|
|
||||||
|
proc newSession(env: GenodeEnv; store: IpldStore; id: SessionId; label, args: string): Session =
|
||||||
|
## Create a new session and packet handling procedure
|
||||||
|
let session = new Session
|
||||||
|
assert(not session.isNil)
|
||||||
|
proc construct(cpp: SessionCpp; env: GenodeEnv; args: cstring) {.importcpp.}
|
||||||
|
session.cpp.construct(env, args)
|
||||||
|
session.store = store
|
||||||
|
session.id = id
|
||||||
|
session.label = label
|
||||||
|
session.sig = env.ep.newSignalHandler do ():
|
||||||
|
proc packetAvail(cpp: SessionCpp): bool {.
|
||||||
|
importcpp: "#->sink().packet_avail()".}
|
||||||
|
proc readyToAck(cpp: SessionCpp): bool {.
|
||||||
|
importcpp: "#->sink().ready_to_ack()".}
|
||||||
|
while session.cpp.packetAvail and session.cpp.readyToAck:
|
||||||
|
proc getPacket(cpp: SessionCpp): IpldPacket {.
|
||||||
|
importcpp: "#->sink().get_packet()".}
|
||||||
|
var pkt = session.cpp.getPacket()
|
||||||
|
session.processPacket pkt
|
||||||
|
proc acknowledgePacket(cpp: SessionCpp; pkt: IpldPacket) {.
|
||||||
|
importcpp: "#->sink().acknowledge_packet(@)".}
|
||||||
|
session.cpp.acknowledgePacket(pkt)
|
||||||
|
|
||||||
|
proc packetHandler(cpp: SessionCpp; cap: SignalContextCapability) {.
|
||||||
|
importcpp: "#->packetHandler(@)".}
|
||||||
|
session.cpp.packetHandler(session.sig.cap)
|
||||||
|
result = session
|
||||||
|
|
||||||
|
proc manage(ep: Entrypoint; s: Session): IpldSessionCapability =
|
||||||
|
## Manage a session from the default entrypoint.
|
||||||
|
proc manage(ep: Entrypoint; cpp: SessionCpp): IpldSessionCapability {.
|
||||||
|
importcpp: "#.manage(*#)".}
|
||||||
|
result = ep.manage(s.cpp)
|
||||||
|
GC_ref s
|
||||||
|
|
||||||
|
proc dissolve(ep: Entrypoint; s: Session) =
|
||||||
|
## Dissolve a session from the entrypoint so that it can be freed.
|
||||||
|
proc dissolve(ep: Entrypoint; cpp: SessionCpp) {.
|
||||||
|
importcpp: "#.dissolve(*#)".}
|
||||||
|
ep.dissolve(s.cpp)
|
||||||
|
destruct(s.cpp)
|
||||||
|
dissolve(s.sig)
|
||||||
|
GC_unref s
|
||||||
|
|
||||||
|
type
|
||||||
|
IpldServer* = ref object
|
||||||
|
env: GenodeEnv
|
||||||
|
store*: IpldStore
|
||||||
|
sessions*: Table[SessionId, Session]
|
||||||
|
|
||||||
|
proc newIpldServer*(env: GenodeEnv; store: IpldStore): IpldServer =
|
||||||
|
IpldServer(
|
||||||
|
env: env, store: store,
|
||||||
|
sessions: initTable[SessionId, Session]())
|
||||||
|
|
||||||
|
proc create*(server: IpldServer; id: SessionId; label, args: string) =
|
||||||
|
if not server.sessions.contains id:
|
||||||
|
try:
|
||||||
|
let
|
||||||
|
session = newSession(server.env, server.store, id, label, args)
|
||||||
|
cap = server.env.ep.manage(session)
|
||||||
|
server.sessions[id] = session
|
||||||
|
proc deliverSession(env: GenodeEnv; id: SessionId; cap: IpldSessionCapability) {.
|
||||||
|
importcpp: "#->parent().deliver_session_cap(Genode::Parent::Server::Id{#}, #)".}
|
||||||
|
server.env.deliverSession(id, cap)
|
||||||
|
echo "session opened for ", label
|
||||||
|
except:
|
||||||
|
echo "failed to create session for '", label, "', ", getCurrentExceptionMsg()
|
||||||
|
server.env.sessionResponseDeny id
|
||||||
|
|
||||||
|
proc close*(server: IpldServer; id: SessionId) =
|
||||||
|
## Close a session at the IPLD server.
|
||||||
|
if server.sessions.contains id:
|
||||||
|
let session = server.sessions[id]
|
||||||
|
server.env.ep.dissolve(session)
|
||||||
|
server.sessions.del id
|
||||||
|
server.env.sessionResponseClose id
|
47
src/ipld/genode/ipldsession.nim
Normal file
47
src/ipld/genode/ipldsession.nim
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#
|
||||||
|
# \brief IPLD session definitions
|
||||||
|
# \author Emery Hemingway
|
||||||
|
# \date 2017-11-11
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 Genode Labs GmbH
|
||||||
|
#
|
||||||
|
# This file is part of the Genode OS framework, which is distributed
|
||||||
|
# under the terms of the GNU Affero General Public License version 3.
|
||||||
|
#
|
||||||
|
|
||||||
|
import ipld
|
||||||
|
|
||||||
|
const MaxPacketSize* = 1 shl 18;
|
||||||
|
|
||||||
|
type
|
||||||
|
IpldSessionCapability* {.final, pure,
|
||||||
|
importcpp: "Ipld::Session_capability",
|
||||||
|
header: "<ipld_session/capability.h>".} = object
|
||||||
|
|
||||||
|
IpldPacket* {.
|
||||||
|
importcpp: "Ipld::Packet",
|
||||||
|
header: "<ipld_session/ipld_session.h>".} = object
|
||||||
|
|
||||||
|
IpldOpcode* {.importcpp: "Ipld::Packet::Opcode".} = enum
|
||||||
|
PUT, GET, INVALID
|
||||||
|
|
||||||
|
IpldError* {.importcpp: "Ipld::Packet::Error".} = enum
|
||||||
|
OK, MISSING, OVERSIZE, FULL, ERROR
|
||||||
|
|
||||||
|
proc size*(pkt: IpldPacket): csize {.importcpp.}
|
||||||
|
## Physical packet size.
|
||||||
|
|
||||||
|
proc cidStr(p: IpldPacket): cstring {.importcpp: "#.cid().string()".}
|
||||||
|
proc cid*(p: IpldPacket): Cid = parseCid $p.cidStr
|
||||||
|
proc setCid*(p: var IpldPacket; cid: cstring) {.importcpp: "#.cid(@)".}
|
||||||
|
proc setCid*(p: var IpldPacket; cid: Cid) = p.setCid(cid.toHex())
|
||||||
|
|
||||||
|
proc operation*(pkt: IpldPacket): IpldOpcode {.importcpp.}
|
||||||
|
proc len*(pkt: IpldPacket): csize {.importcpp: "length".}
|
||||||
|
## Logical packet length.
|
||||||
|
proc setLen*(pkt: var IpldPacket; len: int) {.importcpp: "length".}
|
||||||
|
## Set logical packet length.
|
||||||
|
proc error*(pkt: IpldPacket): IpldError {.importcpp.}
|
||||||
|
proc setError*(pkt: var IpldPacket; err: IpldError) {.importcpp: "error".}
|
47
src/ipld/genode/session.nim
Normal file
47
src/ipld/genode/session.nim
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#
|
||||||
|
# \brief IPLD session definitions
|
||||||
|
# \author Emery Hemingway
|
||||||
|
# \date 2017-11-11
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 Genode Labs GmbH
|
||||||
|
#
|
||||||
|
# This file is part of the Genode OS framework, which is distributed
|
||||||
|
# under the terms of the GNU Affero General Public License version 3.
|
||||||
|
#
|
||||||
|
|
||||||
|
import ipld
|
||||||
|
|
||||||
|
const MaxPacketSize* = 1 shl 18;
|
||||||
|
|
||||||
|
type
|
||||||
|
IpldSessionCapability* {.final, pure,
|
||||||
|
importcpp: "Ipld::Session_capability",
|
||||||
|
header: "<ipld_session/capability.h>".} = object
|
||||||
|
|
||||||
|
IpldPacket* {.
|
||||||
|
importcpp: "Ipld::Packet",
|
||||||
|
header: "<ipld_session/ipld_session.h>".} = object
|
||||||
|
|
||||||
|
IpldOpcode* {.importcpp: "Ipld::Packet::Opcode".} = enum
|
||||||
|
PUT, GET, INVALID
|
||||||
|
|
||||||
|
IpldError* {.importcpp: "Ipld::Packet::Error".} = enum
|
||||||
|
OK, MISSING, OVERSIZE, FULL, ERROR
|
||||||
|
|
||||||
|
proc size*(pkt: IpldPacket): csize {.importcpp.}
|
||||||
|
## Physical packet size.
|
||||||
|
|
||||||
|
proc cidStr(p: IpldPacket): cstring {.importcpp: "#.cid().string()".}
|
||||||
|
proc cid*(p: IpldPacket): Cid = parseCid $p.cidStr
|
||||||
|
proc setCid*(p: var IpldPacket; cid: cstring) {.importcpp: "#.cid(@)".}
|
||||||
|
proc setCid*(p: var IpldPacket; cid: Cid) = p.setCid(cid.toHex())
|
||||||
|
|
||||||
|
proc operation*(pkt: IpldPacket): IpldOpcode {.importcpp.}
|
||||||
|
proc len*(pkt: IpldPacket): csize {.importcpp: "length".}
|
||||||
|
## Logical packet length.
|
||||||
|
proc setLen*(pkt: var IpldPacket; len: int) {.importcpp: "length".}
|
||||||
|
## Set logical packet length.
|
||||||
|
proc error*(pkt: IpldPacket): IpldError {.importcpp.}
|
||||||
|
proc setError*(pkt: var IpldPacket; err: IpldError) {.importcpp: "error".}
|
|
@ -1,6 +1,6 @@
|
||||||
import httpclient, json, base58.bitcoin, streams, nimSHA2, cbor, tables
|
import httpclient, json, base58/bitcoin, streams, nimSHA2, cbor, tables
|
||||||
|
|
||||||
import ipld, multiformats, ipldstore, unixfs
|
import ../ipld, ./multiformats, ./stores, ./unixfs
|
||||||
|
|
||||||
type
|
type
|
||||||
IpfsStore* = ref IpfsStoreObj
|
IpfsStore* = ref IpfsStoreObj
|
|
@ -1,6 +1,6 @@
|
||||||
import streams, os, parseopt
|
import streams, os, parseopt
|
||||||
|
|
||||||
import ipfsdaemon, ipldstore, ipld, unixfs
|
import ../ipld, ./ipfsdaemon, ./stores, ./unixfs
|
||||||
|
|
||||||
proc readFile(store: IpldStore; s: Stream; cid: Cid) =
|
proc readFile(store: IpldStore; s: Stream; cid: Cid) =
|
||||||
var chunk = ""
|
var chunk = ""
|
|
@ -1,6 +1,6 @@
|
||||||
import nre, os, strutils, tables, parseopt, streams, cbor
|
import nre, os, strutils, tables, parseopt, streams, cbor
|
||||||
|
|
||||||
import ipld, ipldstore, unixfs, multiformats
|
import ../ipld, ./stores, ./unixfs, ./multiformats
|
||||||
|
|
||||||
type
|
type
|
||||||
EvalError = object of SystemError
|
EvalError = object of SystemError
|
||||||
|
@ -232,7 +232,7 @@ proc print(ast: NodeRef; s: Stream) =
|
||||||
|
|
||||||
proc readAtom(r: Reader): Atom =
|
proc readAtom(r: Reader): Atom =
|
||||||
let token = r.next
|
let token = r.next
|
||||||
try:
|
block:
|
||||||
if token[token.low] == '"':
|
if token[token.low] == '"':
|
||||||
if token[token.high] != '"':
|
if token[token.high] != '"':
|
||||||
newAtomError("invalid string '$1'" % token)
|
newAtomError("invalid string '$1'" % token)
|
||||||
|
@ -241,12 +241,12 @@ proc readAtom(r: Reader): Atom =
|
||||||
elif token.contains DirSep:
|
elif token.contains DirSep:
|
||||||
# TODO: memoize this, store a table of paths to atoms
|
# TODO: memoize this, store a table of paths to atoms
|
||||||
newAtomPath token
|
newAtomPath token
|
||||||
elif token.len > 48:
|
elif token.len == 46 or token.len > 48:
|
||||||
Atom(kind: atomCid, cid: token.parseCid)
|
Atom(kind: atomCid, cid: token.parseCid)
|
||||||
else:
|
else:
|
||||||
Atom(kind: atomSymbol, sym: token.normalize)
|
Atom(kind: atomSymbol, sym: token.normalize)
|
||||||
except:
|
#except:
|
||||||
newAtomError(getCurrentExceptionMsg())
|
# newAtomError(getCurrentExceptionMsg())
|
||||||
|
|
||||||
proc readForm(r: Reader): NodeRef
|
proc readForm(r: Reader): NodeRef
|
||||||
|
|
||||||
|
@ -399,7 +399,7 @@ proc lsFunc(env: Env; args: NodeObj): NodeRef =
|
||||||
result = newNodeList()
|
result = newNodeList()
|
||||||
for n in args.walk:
|
for n in args.walk:
|
||||||
let a = n.atom
|
let a = n.atom
|
||||||
if a.cid.isDagCbor:
|
if a.cid.isDag:
|
||||||
let ufsNode = env.getUnixfs a.cid
|
let ufsNode = env.getUnixfs a.cid
|
||||||
if ufsNode.isDir:
|
if ufsNode.isDir:
|
||||||
for name, u in ufsNode.items:
|
for name, u in ufsNode.items:
|
||||||
|
@ -410,7 +410,7 @@ proc lsFunc(env: Env; args: NodeObj): NodeRef =
|
||||||
e.append name.newAtomString.newNode
|
e.append name.newAtomString.newNode
|
||||||
result.append e
|
result.append e
|
||||||
else:
|
else:
|
||||||
raiseAssert("ls over a raw IPLD block")
|
raiseAssert("ls over " & $a.cid.codec)
|
||||||
|
|
||||||
proc mapFunc(env: Env; args: NodeObj): NodeRef =
|
proc mapFunc(env: Env; args: NodeObj): NodeRef =
|
||||||
assertArgCount(args, 2)
|
assertArgCount(args, 2)
|
1
src/ipld/ipldrepl.nim.cfg
Normal file
1
src/ipld/ipldrepl.nim.cfg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
-d:ssl
|
|
@ -1,5 +1,4 @@
|
||||||
import streams, strutils, os, ipld, cbor, multiformats, hex,
|
import streams, strutils, os, ../ipld, cbor, ./multiformats, ./store
|
||||||
ipldstore, ipldclient
|
|
||||||
|
|
||||||
type
|
type
|
||||||
IpldReplicator* = ref IpldReplicatorObj
|
IpldReplicator* = ref IpldReplicatorObj
|
|
@ -1,4 +1,6 @@
|
||||||
import streams, strutils, os, ipld, cbor, multiformats, hex
|
import std/streams, std/strutils, std/os
|
||||||
|
import cbor
|
||||||
|
import ../ipld, ./multiformats, ./priv/hex
|
||||||
|
|
||||||
type
|
type
|
||||||
MissingObject* = ref object of SystemError
|
MissingObject* = ref object of SystemError
|
|
@ -1,6 +1,6 @@
|
||||||
import strutils, multiformats, streams, tables, cbor, os, hex, math
|
import strutils, multiformats, streams, tables, cbor, os, math
|
||||||
|
|
||||||
import ipld, ipldstore
|
import ../ipld, ./stores
|
||||||
|
|
||||||
type EntryKey = enum
|
type EntryKey = enum
|
||||||
typeKey = 1,
|
typeKey = 1,
|
Loading…
Reference in New Issue
Block a user