mirror of
https://github.com/logos-storage/nim-leopard.git
synced 2026-01-02 13:43:08 +00:00
Add prepare phase to encode/decode
This commit is contained in:
parent
3e09d8113f
commit
68e691583e
@ -36,6 +36,9 @@ type
|
|||||||
dataBufferPtr: seq[LeoBufferPtr] # buffer where data is copied before encoding
|
dataBufferPtr: seq[LeoBufferPtr] # buffer where data is copied before encoding
|
||||||
workBufferCount: int # number of parity work buffers
|
workBufferCount: int # number of parity work buffers
|
||||||
workBufferPtr: seq[LeoBufferPtr] # buffer where parity data is written during encoding or before decoding
|
workBufferPtr: seq[LeoBufferPtr] # buffer where parity data is written during encoding or before decoding
|
||||||
|
|
||||||
|
dataBufferNil: seq[bool] # true represents Nil in dataBufferPtr
|
||||||
|
workBufferNil: seq[bool] # true represents nil in workBufferPtr
|
||||||
case kind: LeoCoderKind
|
case kind: LeoCoderKind
|
||||||
of LeoCoderKind.Decoder:
|
of LeoCoderKind.Decoder:
|
||||||
decodeBufferCount: int # number of decoding work buffers
|
decodeBufferCount: int # number of decoding work buffers
|
||||||
@ -46,31 +49,33 @@ type
|
|||||||
LeoEncoder* = object of Leo
|
LeoEncoder* = object of Leo
|
||||||
LeoDecoder* = object of Leo
|
LeoDecoder* = object of Leo
|
||||||
|
|
||||||
func encode*(
|
|
||||||
self: var LeoEncoder,
|
|
||||||
data,
|
|
||||||
parity: var openArray[seq[byte]]): Result[void, cstring] =
|
|
||||||
## Encode a list of buffers in `data` into a number of `bufSize` sized
|
|
||||||
## `parity` buffers
|
|
||||||
##
|
|
||||||
## `data` - list of original data `buffers` of size `bufSize`
|
|
||||||
## `parity` - list of parity `buffers` of size `bufSize`
|
|
||||||
##
|
|
||||||
|
|
||||||
|
func prepareEncode*(
|
||||||
|
self: var LeoEncoder,
|
||||||
|
data: var openArray[seq[byte]]
|
||||||
|
): Result[void, cstring] =
|
||||||
|
## Copy `data` into internal encode buffer
|
||||||
|
##
|
||||||
|
|
||||||
if data.len != self.buffers:
|
if data.len != self.buffers:
|
||||||
return err("Number of data buffers should match!")
|
return err("Number of data buffers should match!")
|
||||||
|
|
||||||
if parity.len != self.parity:
|
# copy data into aligned buffer
|
||||||
return err("Number of parity buffers should match!")
|
for i in 0..<self.buffers:
|
||||||
|
copyMem(self.dataBufferPtr[i], addr data[i][0], self.bufSize)
|
||||||
|
|
||||||
|
ok()
|
||||||
|
|
||||||
|
func encodePrepared*(
|
||||||
|
self: var LeoEncoder
|
||||||
|
): Result[void, cstring] =
|
||||||
|
## Encode using previously prepared buffer (using `prepareEncode`)
|
||||||
|
##
|
||||||
|
|
||||||
# zero encode work buffer to avoid corrupting with previous run
|
# zero encode work buffer to avoid corrupting with previous run
|
||||||
for i in 0..<self.workBufferCount:
|
for i in 0..<self.workBufferCount:
|
||||||
zeroMem(self.workBufferPtr[i], self.bufSize)
|
zeroMem(self.workBufferPtr[i], self.bufSize)
|
||||||
|
|
||||||
# copy data into aligned buffer
|
|
||||||
for i in 0..<data.len:
|
|
||||||
copyMem(self.dataBufferPtr[i], addr data[i][0], self.bufSize)
|
|
||||||
|
|
||||||
let
|
let
|
||||||
res = leoEncode(
|
res = leoEncode(
|
||||||
self.bufSize.culonglong,
|
self.bufSize.culonglong,
|
||||||
@ -83,10 +88,138 @@ func encode*(
|
|||||||
if ord(res) != ord(LeopardSuccess):
|
if ord(res) != ord(LeopardSuccess):
|
||||||
return err(leoResultString(res.LeopardResult))
|
return err(leoResultString(res.LeopardResult))
|
||||||
|
|
||||||
|
ok()
|
||||||
|
|
||||||
|
func readParity*(
|
||||||
|
self: var LeoEncoder,
|
||||||
|
parity: var openArray[seq[byte]]
|
||||||
|
): Result[void, cstring] =
|
||||||
|
## Copies previously encoded parity data into `parity` buffer
|
||||||
|
##
|
||||||
|
|
||||||
|
if parity.len != self.parity:
|
||||||
|
return err("Number of parity buffers should match!")
|
||||||
|
|
||||||
for i in 0..<parity.len:
|
for i in 0..<parity.len:
|
||||||
copyMem(addr parity[i][0], self.workBufferPtr[i], self.bufSize)
|
copyMem(addr parity[i][0], self.workBufferPtr[i], self.bufSize)
|
||||||
|
|
||||||
return ok()
|
ok()
|
||||||
|
|
||||||
|
func encode*(
|
||||||
|
self: var LeoEncoder,
|
||||||
|
data,
|
||||||
|
parity: var openArray[seq[byte]]): Result[void, cstring] =
|
||||||
|
## Encode a list of buffers in `data` into a number of `bufSize` sized
|
||||||
|
## `parity` buffers
|
||||||
|
##
|
||||||
|
## `data` - list of original data `buffers` of size `bufSize`
|
||||||
|
## `parity` - list of parity `buffers` of size `bufSize`
|
||||||
|
##
|
||||||
|
|
||||||
|
let res = self.prepareEncode(data)
|
||||||
|
|
||||||
|
if res.isErr():
|
||||||
|
return res
|
||||||
|
|
||||||
|
let res2 = self.encodePrepared()
|
||||||
|
|
||||||
|
if res2.isErr():
|
||||||
|
return res2
|
||||||
|
|
||||||
|
self.readParity(parity)
|
||||||
|
|
||||||
|
func prepareDecode*(
|
||||||
|
self: var LeoDecoder,
|
||||||
|
data,
|
||||||
|
parity: var openArray[seq[byte]]
|
||||||
|
): Result[void, cstring] =
|
||||||
|
|
||||||
|
if data.len != self.buffers:
|
||||||
|
return err("Number of data buffers should match!")
|
||||||
|
|
||||||
|
if parity.len != self.parity:
|
||||||
|
return err("Number of parity buffers should match!")
|
||||||
|
|
||||||
|
# clean out work and data buffers
|
||||||
|
for i in 0..<self.buffers:
|
||||||
|
zeroMem(self.dataBufferPtr[i], self.bufSize)
|
||||||
|
|
||||||
|
for i in 0..<self.workBufferCount:
|
||||||
|
zeroMem(self.workBufferPtr[i], self.bufSize)
|
||||||
|
|
||||||
|
# copy data into aligned buffer
|
||||||
|
for i in 0..<data.len:
|
||||||
|
if data[i].len > 0:
|
||||||
|
copyMem(self.dataBufferPtr[i], addr data[i][0], self.bufSize)
|
||||||
|
self.dataBufferNil[i] = false
|
||||||
|
else:
|
||||||
|
self.dataBufferNil[i] = true
|
||||||
|
|
||||||
|
# copy parity into aligned buffer
|
||||||
|
for i in 0..<self.workBufferCount:
|
||||||
|
if i < parity.len and parity[i].len > 0:
|
||||||
|
copyMem(self.workBufferPtr[i], addr parity[i][0], self.bufSize)
|
||||||
|
self.workBufferNil[i] = false
|
||||||
|
else:
|
||||||
|
self.workBufferNil[i] = true
|
||||||
|
|
||||||
|
ok()
|
||||||
|
|
||||||
|
func decodePrepared*(
|
||||||
|
self: var LeoDecoder
|
||||||
|
): Result[void, cstring] =
|
||||||
|
|
||||||
|
for i in 0..<self.decodeBufferCount:
|
||||||
|
zeroMem(self.decodeBufferPtr[i], self.bufSize)
|
||||||
|
|
||||||
|
# this is needed because erasures are nil pointers
|
||||||
|
var
|
||||||
|
dataPtr = newSeq[LeoBufferPtr](self.buffers)
|
||||||
|
parityPtr = newSeq[LeoBufferPtr](self.workBufferCount)
|
||||||
|
|
||||||
|
# copy data into aligned buffer
|
||||||
|
for i in 0..<self.buffers:
|
||||||
|
if self.dataBufferNil[i]:
|
||||||
|
dataPtr[i] = nil
|
||||||
|
else:
|
||||||
|
dataPtr[i] = self.dataBufferPtr[i]
|
||||||
|
|
||||||
|
# copy parity into aligned buffer
|
||||||
|
for i in 0..<self.workBufferCount:
|
||||||
|
if self.workBufferNil[i]:
|
||||||
|
parityPtr[i] = nil
|
||||||
|
else:
|
||||||
|
parityPtr[i] = self.workBufferPtr[i]
|
||||||
|
|
||||||
|
let
|
||||||
|
res = leoDecode(
|
||||||
|
self.bufSize.culonglong,
|
||||||
|
self.buffers.cuint,
|
||||||
|
self.parity.cuint,
|
||||||
|
self.decodeBufferCount.cuint,
|
||||||
|
cast[LeoDataPtr](addr dataPtr[0]),
|
||||||
|
cast[LeoDataPtr](addr parityPtr[0]),
|
||||||
|
cast[ptr pointer](addr self.decodeBufferPtr[0]))
|
||||||
|
|
||||||
|
if ord(res) != ord(LeopardSuccess):
|
||||||
|
return err(leoResultString(res.LeopardResult))
|
||||||
|
|
||||||
|
ok()
|
||||||
|
|
||||||
|
func readDecoded*(
|
||||||
|
self: var LeoDecoder,
|
||||||
|
recovered: var openArray[seq[byte]]
|
||||||
|
): Result[void, cstring] =
|
||||||
|
|
||||||
|
if recovered.len != self.buffers:
|
||||||
|
return err("Number of recovered buffers should match buffers!")
|
||||||
|
|
||||||
|
for i, wasNil in self.dataBufferNil:
|
||||||
|
if wasNil:
|
||||||
|
copyMem(addr recovered[i][0], self.decodeBufferPtr[i], self.bufSize)
|
||||||
|
|
||||||
|
ok()
|
||||||
|
|
||||||
|
|
||||||
func decode*(
|
func decode*(
|
||||||
self: var LeoDecoder,
|
self: var LeoDecoder,
|
||||||
@ -102,66 +235,25 @@ func decode*(
|
|||||||
## `recovered` - list of recovered `buffers` of size `bufSize`
|
## `recovered` - list of recovered `buffers` of size `bufSize`
|
||||||
##
|
##
|
||||||
|
|
||||||
if data.len != self.buffers:
|
let res = self.prepareDecode(data, parity)
|
||||||
return err("Number of data buffers should match!")
|
|
||||||
|
|
||||||
if parity.len != self.parity:
|
if res.isErr():
|
||||||
return err("Number of parity buffers should match!")
|
return res
|
||||||
|
|
||||||
if recovered.len != self.buffers:
|
let res2 = self.decodePrepared()
|
||||||
return err("Number of recovered buffers should match buffers!")
|
|
||||||
|
|
||||||
# clean out work and data buffers
|
if res2.isErr():
|
||||||
for i in 0..<self.workBufferCount:
|
return res2
|
||||||
zeroMem(self.workBufferPtr[i], self.bufSize)
|
|
||||||
|
|
||||||
for i in 0..<self.decodeBufferCount:
|
self.readDecoded(recovered)
|
||||||
zeroMem(self.decodeBufferPtr[i], self.bufSize)
|
|
||||||
|
|
||||||
for i in 0..<data.len:
|
|
||||||
zeroMem(self.dataBufferPtr[i], self.bufSize)
|
|
||||||
|
|
||||||
# this is needed because erasures are nil pointers
|
|
||||||
var
|
|
||||||
dataPtr = newSeq[LeoBufferPtr](data.len)
|
|
||||||
parityPtr = newSeq[LeoBufferPtr](self.workBufferCount)
|
|
||||||
|
|
||||||
# copy data into aligned buffer
|
|
||||||
for i in 0..<data.len:
|
|
||||||
if data[i].len > 0:
|
|
||||||
copyMem(self.dataBufferPtr[i], addr data[i][0], self.bufSize)
|
|
||||||
dataPtr[i] = self.dataBufferPtr[i]
|
|
||||||
else:
|
|
||||||
dataPtr[i] = nil
|
|
||||||
|
|
||||||
# copy parity into aligned buffer
|
|
||||||
for i in 0..<self.workBufferCount:
|
|
||||||
if i < parity.len and parity[i].len > 0:
|
|
||||||
copyMem(self.workBufferPtr[i], addr parity[i][0], self.bufSize)
|
|
||||||
parityPtr[i] = self.workBufferPtr[i]
|
|
||||||
else:
|
|
||||||
parityPtr[i] = nil
|
|
||||||
|
|
||||||
let
|
|
||||||
res = leoDecode(
|
|
||||||
self.bufSize.culonglong,
|
|
||||||
self.buffers.cuint,
|
|
||||||
self.parity.cuint,
|
|
||||||
self.decodeBufferCount.cuint,
|
|
||||||
cast[LeoDataPtr](addr dataPtr[0]),
|
|
||||||
cast[LeoDataPtr](addr parityPtr[0]),
|
|
||||||
cast[ptr pointer](addr self.decodeBufferPtr[0]))
|
|
||||||
|
|
||||||
if ord(res) != ord(LeopardSuccess):
|
|
||||||
return err(leoResultString(res.LeopardResult))
|
|
||||||
|
|
||||||
for i, p in dataPtr:
|
|
||||||
if p.isNil:
|
|
||||||
copyMem(addr recovered[i][0], self.decodeBufferPtr[i], self.bufSize)
|
|
||||||
|
|
||||||
ok()
|
|
||||||
|
|
||||||
func free*(self: var Leo) =
|
func free*(self: var Leo) =
|
||||||
|
if self.dataBufferNil.len > 0:
|
||||||
|
self.dataBufferNil.setLen(0)
|
||||||
|
|
||||||
|
if self.workBufferNil.len > 0:
|
||||||
|
self.workBufferNil.setLen(0)
|
||||||
|
|
||||||
if self.workBufferPtr.len > 0:
|
if self.workBufferPtr.len > 0:
|
||||||
for i, p in self.workBufferPtr:
|
for i, p in self.workBufferPtr:
|
||||||
if not isNil(p):
|
if not isNil(p):
|
||||||
@ -232,6 +324,9 @@ proc init[TT: Leo](
|
|||||||
buffers.cuint,
|
buffers.cuint,
|
||||||
parity.cuint).int
|
parity.cuint).int
|
||||||
|
|
||||||
|
self.workBufferNil.setLen(self.workBufferCount)
|
||||||
|
self.dataBufferNil.setLen(self.buffers)
|
||||||
|
|
||||||
# initialize encode work buffers
|
# initialize encode work buffers
|
||||||
for _ in 0..<self.workBufferCount:
|
for _ in 0..<self.workBufferCount:
|
||||||
self.workBufferPtr.add(cast[LeoBufferPtr](self.bufSize.leoAlloc()))
|
self.workBufferPtr.add(cast[LeoBufferPtr](self.bufSize.leoAlloc()))
|
||||||
|
|||||||
@ -96,6 +96,8 @@ proc testPackets*(
|
|||||||
if parityLosses > 0:
|
if parityLosses > 0:
|
||||||
dropRandomIdx(parityBuf, parityLosses)
|
dropRandomIdx(parityBuf, parityLosses)
|
||||||
|
|
||||||
|
GC_fullCollect()
|
||||||
|
|
||||||
decoder.decode(dataBuf, parityBuf, recoveredBuf).tryGet()
|
decoder.decode(dataBuf, parityBuf, recoveredBuf).tryGet()
|
||||||
|
|
||||||
for i, d in dataBuf:
|
for i, d in dataBuf:
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import std/random
|
import std/random
|
||||||
import std/sets
|
import std/sets
|
||||||
|
import std/sequtils
|
||||||
|
|
||||||
import pkg/unittest2
|
import pkg/unittest2
|
||||||
import pkg/stew/results
|
import pkg/stew/results
|
||||||
@ -31,8 +32,8 @@ suite "Leopard Parametrization":
|
|||||||
test "Should not allow encoding with invalid data buffer counts":
|
test "Should not allow encoding with invalid data buffer counts":
|
||||||
var
|
var
|
||||||
leo = LeoEncoder.init(64, 4, 2).tryGet()
|
leo = LeoEncoder.init(64, 4, 2).tryGet()
|
||||||
data = newSeq[seq[byte]](3)
|
data = newSeqWith[seq[byte]](3, newSeq[byte](64))
|
||||||
parity = newSeq[seq[byte]](2)
|
parity = newSeqWith[seq[byte]](2, newSeq[byte](64))
|
||||||
|
|
||||||
check:
|
check:
|
||||||
leo.encode(data, parity).error == "Number of data buffers should match!"
|
leo.encode(data, parity).error == "Number of data buffers should match!"
|
||||||
@ -40,8 +41,8 @@ suite "Leopard Parametrization":
|
|||||||
test "Should not allow encoding with invalid parity buffer counts":
|
test "Should not allow encoding with invalid parity buffer counts":
|
||||||
var
|
var
|
||||||
leo = LeoEncoder.init(64, 4, 2).tryGet()
|
leo = LeoEncoder.init(64, 4, 2).tryGet()
|
||||||
data = newSeq[seq[byte]](4)
|
data = newSeqWith[seq[byte]](4, newSeq[byte](64))
|
||||||
parity = newSeq[seq[byte]](3)
|
parity = newSeqWith[seq[byte]](3, newSeq[byte](64))
|
||||||
|
|
||||||
check:
|
check:
|
||||||
leo.encode(data, parity).error == "Number of parity buffers should match!"
|
leo.encode(data, parity).error == "Number of parity buffers should match!"
|
||||||
@ -49,9 +50,9 @@ suite "Leopard Parametrization":
|
|||||||
test "Should not allow decoding with invalid data buffer counts":
|
test "Should not allow decoding with invalid data buffer counts":
|
||||||
var
|
var
|
||||||
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
||||||
data = newSeq[seq[byte]](3)
|
data = newSeqWith[seq[byte]](3, newSeq[byte](64))
|
||||||
parity = newSeq[seq[byte]](2)
|
parity = newSeqWith[seq[byte]](2, newSeq[byte](64))
|
||||||
recovered = newSeq[seq[byte]](3)
|
recovered = newSeqWith[seq[byte]](3, newSeq[byte](64))
|
||||||
|
|
||||||
check:
|
check:
|
||||||
leo.decode(data, parity, recovered).error == "Number of data buffers should match!"
|
leo.decode(data, parity, recovered).error == "Number of data buffers should match!"
|
||||||
@ -59,9 +60,9 @@ suite "Leopard Parametrization":
|
|||||||
test "Should not allow decoding with invalid data buffer counts":
|
test "Should not allow decoding with invalid data buffer counts":
|
||||||
var
|
var
|
||||||
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
||||||
data = newSeq[seq[byte]](4)
|
data = newSeqWith[seq[byte]](4, newSeq[byte](64))
|
||||||
parity = newSeq[seq[byte]](1)
|
parity = newSeqWith[seq[byte]](1, newSeq[byte](64))
|
||||||
recovered = newSeq[seq[byte]](3)
|
recovered = newSeqWith[seq[byte]](3, newSeq[byte](64))
|
||||||
|
|
||||||
check:
|
check:
|
||||||
leo.decode(data, parity, recovered).error == "Number of parity buffers should match!"
|
leo.decode(data, parity, recovered).error == "Number of parity buffers should match!"
|
||||||
@ -69,9 +70,9 @@ suite "Leopard Parametrization":
|
|||||||
test "Should not allow decoding with invalid data buffer counts":
|
test "Should not allow decoding with invalid data buffer counts":
|
||||||
var
|
var
|
||||||
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
||||||
data = newSeq[seq[byte]](4)
|
data = newSeqWith[seq[byte]](4, newSeq[byte](64))
|
||||||
parity = newSeq[seq[byte]](2)
|
parity = newSeqWith[seq[byte]](2, newSeq[byte](64))
|
||||||
recovered = newSeq[seq[byte]](3)
|
recovered = newSeqWith[seq[byte]](3, newSeq[byte](64))
|
||||||
|
|
||||||
check:
|
check:
|
||||||
leo.decode(data, parity, recovered).error == "Number of recovered buffers should match buffers!"
|
leo.decode(data, parity, recovered).error == "Number of recovered buffers should match buffers!"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user