mirror of
https://github.com/logos-storage/nim-leopard.git
synced 2026-01-02 13:43:08 +00:00
chore: orc support
Refactor public API to use `var openArray[seq[byte]]` instead of raw pointers for ORC compatibility. The previous API required callers to extract raw pointers from seqs before calling encode/decode. With refc and shallowCopy, reference counting kept buffers alive. ORC doesn't track raw pointers and can reallocate seq buffers at yield points, leaving pointers dangling. The new openArray API borrows data safely. Also updates CI to Nim 2.2.4 and bumps version to 0.2.0. Part of https://github.com/logos-storage/nim-leopard/issues/24 Signed-off-by: Chrysostomos Nanakos <chris@include.gr>
This commit is contained in:
parent
0478b12df9
commit
f1878d6aaa
7
.github/workflows/test.yml
vendored
7
.github/workflows/test.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
runner: windows-latest
|
runner: windows-latest
|
||||||
}
|
}
|
||||||
# Earliest supported and latest nim
|
# Earliest supported and latest nim
|
||||||
nim: [1.6.18, "stable"]
|
nim: [binary:2.2.4]
|
||||||
name: ${{ matrix.platform.icon }} ${{ matrix.platform.label }} - Nim v${{ matrix.nim }}
|
name: ${{ matrix.platform.icon }} ${{ matrix.platform.label }} - Nim v${{ matrix.nim }}
|
||||||
runs-on: ${{ matrix.platform.runner }}
|
runs-on: ${{ matrix.platform.runner }}
|
||||||
defaults:
|
defaults:
|
||||||
@ -33,10 +33,9 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- uses: jiro4989/setup-nim-action@v2
|
- uses: iffy/install-nim@v5
|
||||||
with:
|
with:
|
||||||
nim-version: ${{matrix.nim}}
|
version: ${{ matrix.nim }}
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: nimble install -y
|
run: nimble install -y
|
||||||
- name: Build and run tests
|
- name: Build and run tests
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
mode = ScriptMode.Verbose
|
mode = ScriptMode.Verbose
|
||||||
|
|
||||||
packageName = "leopard"
|
packageName = "leopard"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
author = "Status Research & Development GmbH"
|
author = "Status Research & Development GmbH"
|
||||||
description = "A wrapper for Leopard-RS"
|
description = "A wrapper for Leopard-RS"
|
||||||
license = "Apache License 2.0 or MIT"
|
license = "Apache License 2.0 or MIT"
|
||||||
|
|||||||
@ -16,27 +16,27 @@ import ./utils
|
|||||||
|
|
||||||
export wrapper, results
|
export wrapper, results
|
||||||
|
|
||||||
const
|
const BuffMultiples* = 64
|
||||||
BuffMultiples* = 64
|
|
||||||
|
|
||||||
type
|
type
|
||||||
LeoBufferPtr* = ptr UncheckedArray[byte]
|
LeoBufferPtr* = ptr UncheckedArray[byte]
|
||||||
|
|
||||||
LeoCoderKind* {.pure.} = enum
|
LeoCoderKind* {.pure.} = enum
|
||||||
Encoder,
|
Encoder
|
||||||
Decoder
|
Decoder
|
||||||
|
|
||||||
Leo* = object of RootObj
|
Leo* = object of RootObj
|
||||||
bufSize*: int # size of the buffer in multiples of 64
|
bufSize*: int # size of the buffer in multiples of 64
|
||||||
buffers*: int # total number of data buffers (K)
|
buffers*: int # total number of data buffers (K)
|
||||||
parity*: int # total number of parity buffers (M)
|
parity*: int # total number of parity buffers (M)
|
||||||
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
|
||||||
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
|
||||||
decodeBufferPtr: seq[LeoBufferPtr] # work buffer used for decoding
|
decodeBufferPtr: seq[LeoBufferPtr] # work buffer used for decoding
|
||||||
of LeoCoderKind.Encoder:
|
of LeoCoderKind.Encoder:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
@ -44,9 +44,8 @@ type
|
|||||||
LeoDecoder* = object of Leo
|
LeoDecoder* = object of Leo
|
||||||
|
|
||||||
func encode*(
|
func encode*(
|
||||||
self: var LeoEncoder,
|
self: var LeoEncoder, data, parity: var openArray[seq[byte]]
|
||||||
data,parity: ptr UncheckedArray[ptr UncheckedArray[byte]],
|
): Result[void, cstring] =
|
||||||
dataLen,parityLen: int ): Result[void, cstring] =
|
|
||||||
## Encode a list of buffers in `data` into a number of `bufSize` sized
|
## Encode a list of buffers in `data` into a number of `bufSize` sized
|
||||||
## `parity` buffers
|
## `parity` buffers
|
||||||
##
|
##
|
||||||
@ -54,43 +53,40 @@ func encode*(
|
|||||||
## `parity` - list of parity `buffers` of size `bufSize`
|
## `parity` - list of parity `buffers` of size `bufSize`
|
||||||
##
|
##
|
||||||
|
|
||||||
if dataLen != self.buffers:
|
if data.len != self.buffers:
|
||||||
return err("Number of data buffers should match!")
|
return err("Number of data buffers should match!")
|
||||||
|
|
||||||
if parityLen != self.parity:
|
if parity.len != self.parity:
|
||||||
return err("Number of parity buffers should match!")
|
return err("Number of parity buffers should match!")
|
||||||
|
|
||||||
# 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
|
# copy data into aligned buffer
|
||||||
for i in 0..<dataLen:
|
for i in 0 ..< data.len:
|
||||||
copyMem(self.dataBufferPtr[i], addr data[i][0], self.bufSize)
|
copyMem(self.dataBufferPtr[i], addr data[i][0], self.bufSize)
|
||||||
|
|
||||||
let
|
let res = leoEncode(
|
||||||
res = leoEncode(
|
self.bufSize.culonglong,
|
||||||
self.bufSize.culonglong,
|
self.buffers.cuint,
|
||||||
self.buffers.cuint,
|
self.parity.cuint,
|
||||||
self.parity.cuint,
|
self.workBufferCount.cuint,
|
||||||
self.workBufferCount.cuint,
|
cast[LeoDataPtr](addr self.dataBufferPtr[0]),
|
||||||
cast[LeoDataPtr](addr self.dataBufferPtr[0]),
|
cast[ptr pointer](addr self.workBufferPtr[0]),
|
||||||
cast[ptr pointer](addr self.workBufferPtr[0]))
|
)
|
||||||
|
|
||||||
if ord(res) != ord(LeopardSuccess):
|
if ord(res) != ord(LeopardSuccess):
|
||||||
return err(leoResultString(res.LeopardResult))
|
return err(leoResultString(res.LeopardResult))
|
||||||
|
|
||||||
for i in 0..<parityLen:
|
for i in 0 ..< parity.len:
|
||||||
copyMem(parity[i], self.workBufferPtr[i], self.bufSize)
|
copyMem(addr parity[i][0], self.workBufferPtr[i], self.bufSize)
|
||||||
|
|
||||||
return ok()
|
return ok()
|
||||||
|
|
||||||
func decode*(
|
func decode*(
|
||||||
self: var LeoDecoder,
|
self: var LeoDecoder, data, parity, recovered: var openArray[seq[byte]]
|
||||||
data,
|
): Result[void, cstring] =
|
||||||
parity,
|
|
||||||
recovered: ptr UncheckedArray[ptr UncheckedArray[byte]],
|
|
||||||
dataLen,parityLen,recoveredLen: int): Result[void, cstring] =
|
|
||||||
## Decode a list of buffers in `data` and `parity` into a list
|
## Decode a list of buffers in `data` and `parity` into a list
|
||||||
## of `recovered` buffers of `bufSize`. The list of `recovered`
|
## of `recovered` buffers of `bufSize`. The list of `recovered`
|
||||||
## buffers should be match the `Leo.buffers`
|
## buffers should be match the `Leo.buffers`
|
||||||
@ -100,55 +96,55 @@ func decode*(
|
|||||||
## `recovered` - list of recovered `buffers` of size `bufSize`
|
## `recovered` - list of recovered `buffers` of size `bufSize`
|
||||||
##
|
##
|
||||||
|
|
||||||
if dataLen != self.buffers:
|
if data.len != self.buffers:
|
||||||
return err("Number of data buffers should match!")
|
return err("Number of data buffers should match!")
|
||||||
|
|
||||||
if parityLen != self.parity:
|
if parity.len != self.parity:
|
||||||
return err("Number of parity buffers should match!")
|
return err("Number of parity buffers should match!")
|
||||||
|
|
||||||
if recoveredLen != self.buffers:
|
if recovered.len != self.buffers:
|
||||||
return err("Number of recovered buffers should match buffers!")
|
return err("Number of recovered buffers should match buffers!")
|
||||||
|
|
||||||
# clean out work and data buffers
|
# clean out work and data buffers
|
||||||
for i in 0..<self.workBufferCount:
|
for i in 0 ..< self.workBufferCount:
|
||||||
zeroMem(self.workBufferPtr[i], self.bufSize)
|
zeroMem(self.workBufferPtr[i], self.bufSize)
|
||||||
|
|
||||||
for i in 0..<self.decodeBufferCount:
|
for i in 0 ..< self.decodeBufferCount:
|
||||||
zeroMem(self.decodeBufferPtr[i], self.bufSize)
|
zeroMem(self.decodeBufferPtr[i], self.bufSize)
|
||||||
|
|
||||||
for i in 0..<dataLen:
|
for i in 0 ..< data.len:
|
||||||
zeroMem(self.dataBufferPtr[i], self.bufSize)
|
zeroMem(self.dataBufferPtr[i], self.bufSize)
|
||||||
|
|
||||||
# this is needed because erasures are nil pointers
|
# this is needed because erasures are nil pointers
|
||||||
var
|
var
|
||||||
dataPtr = newSeq[LeoBufferPtr](dataLen)
|
dataPtr = newSeq[LeoBufferPtr](data.len)
|
||||||
parityPtr = newSeq[LeoBufferPtr](self.workBufferCount)
|
parityPtr = newSeq[LeoBufferPtr](self.workBufferCount)
|
||||||
|
|
||||||
# copy data into aligned buffer
|
# copy data into aligned buffer
|
||||||
for i in 0..<dataLen:
|
for i in 0 ..< data.len:
|
||||||
if not data[i].isNil:
|
if data[i].len > 0:
|
||||||
copyMem(self.dataBufferPtr[i],addr data[i][0], self.bufSize)
|
copyMem(self.dataBufferPtr[i], addr data[i][0], self.bufSize)
|
||||||
dataPtr[i] = self.dataBufferPtr[i]
|
dataPtr[i] = self.dataBufferPtr[i]
|
||||||
else:
|
else:
|
||||||
dataPtr[i] = nil
|
dataPtr[i] = nil
|
||||||
|
|
||||||
# copy parity into aligned buffer
|
# copy parity into aligned buffer
|
||||||
for i in 0..<self.workBufferCount:
|
for i in 0 ..< self.workBufferCount:
|
||||||
if i < parityLen and not parity[i].isNil:
|
if i < parity.len and parity[i].len > 0:
|
||||||
copyMem(self.workBufferPtr[i], addr parity[i][0], self.bufSize)
|
copyMem(self.workBufferPtr[i], addr parity[i][0], self.bufSize)
|
||||||
parityPtr[i] = self.workBufferPtr[i]
|
parityPtr[i] = self.workBufferPtr[i]
|
||||||
else:
|
else:
|
||||||
parityPtr[i] = nil
|
parityPtr[i] = nil
|
||||||
|
|
||||||
let
|
let res = leoDecode(
|
||||||
res = leoDecode(
|
self.bufSize.culonglong,
|
||||||
self.bufSize.culonglong,
|
self.buffers.cuint,
|
||||||
self.buffers.cuint,
|
self.parity.cuint,
|
||||||
self.parity.cuint,
|
self.decodeBufferCount.cuint,
|
||||||
self.decodeBufferCount.cuint,
|
cast[LeoDataPtr](addr dataPtr[0]),
|
||||||
cast[LeoDataPtr](addr dataPtr[0]),
|
cast[LeoDataPtr](addr parityPtr[0]),
|
||||||
cast[LeoDataPtr](addr parityPtr[0]),
|
cast[ptr pointer](addr self.decodeBufferPtr[0]),
|
||||||
cast[ptr pointer](addr self.decodeBufferPtr[0]))
|
)
|
||||||
|
|
||||||
if ord(res) != ord(LeopardSuccess):
|
if ord(res) != ord(LeopardSuccess):
|
||||||
return err(leoResultString(res.LeopardResult))
|
return err(leoResultString(res.LeopardResult))
|
||||||
@ -193,11 +189,8 @@ func free*(self: var Leo) =
|
|||||||
# self.free()
|
# self.free()
|
||||||
|
|
||||||
proc init[TT: Leo](
|
proc init[TT: Leo](
|
||||||
T: type TT,
|
T: type TT, bufSize, buffers, parity: int, kind: LeoCoderKind
|
||||||
bufSize,
|
): Result[T, cstring] =
|
||||||
buffers,
|
|
||||||
parity: int,
|
|
||||||
kind: LeoCoderKind): Result[T, cstring] =
|
|
||||||
if bufSize mod BuffMultiples != 0:
|
if bufSize mod BuffMultiples != 0:
|
||||||
return err("bufSize should be multiples of 64 bytes!")
|
return err("bufSize should be multiples of 64 bytes!")
|
||||||
|
|
||||||
@ -219,46 +212,33 @@ proc init[TT: Leo](
|
|||||||
if (let res = leoInit(); res.ord != LeopardSuccess.ord):
|
if (let res = leoInit(); res.ord != LeopardSuccess.ord):
|
||||||
return err(leoResultString(res.LeopardResult))
|
return err(leoResultString(res.LeopardResult))
|
||||||
|
|
||||||
var
|
var self = T(kind: kind, bufSize: bufSize, buffers: buffers, parity: parity)
|
||||||
self = T(
|
|
||||||
kind: kind,
|
|
||||||
bufSize: bufSize,
|
|
||||||
buffers: buffers,
|
|
||||||
parity: parity)
|
|
||||||
|
|
||||||
self.workBufferCount = leoEncodeWorkCount(
|
self.workBufferCount = leoEncodeWorkCount(buffers.cuint, parity.cuint).int
|
||||||
buffers.cuint,
|
|
||||||
parity.cuint).int
|
|
||||||
|
|
||||||
# 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()))
|
||||||
|
|
||||||
# initialize data buffers
|
# initialize data buffers
|
||||||
for _ in 0..<self.buffers:
|
for _ in 0 ..< self.buffers:
|
||||||
self.dataBufferPtr.add(cast[LeoBufferPtr](self.bufSize.leoAlloc()))
|
self.dataBufferPtr.add(cast[LeoBufferPtr](self.bufSize.leoAlloc()))
|
||||||
|
|
||||||
if self.kind == LeoCoderKind.Decoder:
|
if self.kind == LeoCoderKind.Decoder:
|
||||||
self.decodeBufferCount = leoDecodeWorkCount(
|
self.decodeBufferCount = leoDecodeWorkCount(buffers.cuint, parity.cuint).int
|
||||||
buffers.cuint,
|
|
||||||
parity.cuint).int
|
|
||||||
|
|
||||||
# initialize decode work buffers
|
# initialize decode work buffers
|
||||||
for _ in 0..<self.decodeBufferCount:
|
for _ in 0 ..< self.decodeBufferCount:
|
||||||
self.decodeBufferPtr.add(cast[LeoBufferPtr](self.bufSize.leoAlloc()))
|
self.decodeBufferPtr.add(cast[LeoBufferPtr](self.bufSize.leoAlloc()))
|
||||||
|
|
||||||
ok(self)
|
ok(self)
|
||||||
|
|
||||||
proc init*(
|
proc init*(
|
||||||
T: type LeoEncoder,
|
T: type LeoEncoder, bufSize, buffers, parity: int
|
||||||
bufSize,
|
): Result[LeoEncoder, cstring] =
|
||||||
buffers,
|
|
||||||
parity: int): Result[LeoEncoder, cstring] =
|
|
||||||
LeoEncoder.init(bufSize, buffers, parity, LeoCoderKind.Encoder)
|
LeoEncoder.init(bufSize, buffers, parity, LeoCoderKind.Encoder)
|
||||||
|
|
||||||
proc init*(
|
proc init*(
|
||||||
T: type LeoDecoder,
|
T: type LeoDecoder, bufSize, buffers, parity: int
|
||||||
bufSize,
|
): Result[LeoDecoder, cstring] =
|
||||||
buffers,
|
|
||||||
parity: int): Result[LeoDecoder, cstring] =
|
|
||||||
LeoDecoder.init(bufSize, buffers, parity, LeoCoderKind.Decoder)
|
LeoDecoder.init(bufSize, buffers, parity, LeoCoderKind.Decoder)
|
||||||
|
|||||||
@ -24,22 +24,22 @@ proc randomCRCPacket*(data: var openArray[byte]) =
|
|||||||
|
|
||||||
copyMem(addr data[4], unsafeAddr crc, sizeof(crc))
|
copyMem(addr data[4], unsafeAddr crc, sizeof(crc))
|
||||||
|
|
||||||
proc checkCRCPacket*(data: ptr UncheckedArray[byte], len: int): bool =
|
proc checkCRCPacket*(data: openArray[byte]): bool =
|
||||||
if len < 16:
|
if data.len < 16:
|
||||||
for i in 1..<len:
|
for d in data[1 .. data.high]:
|
||||||
if data[i] != data[0]:
|
if d != data[0]:
|
||||||
raise (ref Defect)(msg: "Packet don't match")
|
raise (ref Defect)(msg: "Packet don't match")
|
||||||
else:
|
else:
|
||||||
var
|
var
|
||||||
crc = len.uint32
|
crc = data.len.uint32
|
||||||
packCrc: uint32
|
packCrc: uint32
|
||||||
packSize: uint32
|
packSize: uint32
|
||||||
|
|
||||||
copyMem(addr packSize, unsafeAddr data[0], sizeof(packSize))
|
copyMem(addr packSize, unsafeAddr data[0], sizeof(packSize))
|
||||||
if packSize != len.uint:
|
if packSize != data.len.uint:
|
||||||
raise (ref Defect)(msg: "Packet size don't match!")
|
raise (ref Defect)(msg: "Packet size don't match!")
|
||||||
|
|
||||||
for i in 4..<len:
|
for i in 4..< data.len:
|
||||||
let v = data[i]
|
let v = data[i]
|
||||||
crc = (crc shl 3) and (crc shr (32 - 3))
|
crc = (crc shl 3) and (crc shr (32 - 3))
|
||||||
crc += v
|
crc += v
|
||||||
@ -49,43 +49,19 @@ proc checkCRCPacket*(data: ptr UncheckedArray[byte], len: int): bool =
|
|||||||
if packCrc == crc:
|
if packCrc == crc:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc dropRandomIdx*(bufs: ptr UncheckedArray[ptr UncheckedArray[byte]], bufsLen,dropCount: int) =
|
proc dropRandomIdx*(bufs: var openArray[seq[byte]], dropCount: int) =
|
||||||
var
|
var
|
||||||
count = 0
|
count = 0
|
||||||
dups: seq[int]
|
dups: seq[int]
|
||||||
size = bufsLen
|
size = bufs.len
|
||||||
|
|
||||||
while count < dropCount:
|
while count < dropCount:
|
||||||
let i = rand(0..<size)
|
let i = rand(0..<size)
|
||||||
if dups.find(i) == -1:
|
if dups.find(i) == -1:
|
||||||
dups.add(i)
|
dups.add(i)
|
||||||
bufs[i]=nil
|
bufs[i].setLen(0)
|
||||||
count.inc
|
count.inc
|
||||||
|
|
||||||
proc createDoubleArray*(
|
|
||||||
outerLen, innerLen: int
|
|
||||||
): ptr UncheckedArray[ptr UncheckedArray[byte]] =
|
|
||||||
# Allocate outer array
|
|
||||||
result = cast[ptr UncheckedArray[ptr UncheckedArray[byte]]](alloc0(
|
|
||||||
sizeof(ptr UncheckedArray[byte]) * outerLen
|
|
||||||
))
|
|
||||||
|
|
||||||
# Allocate each inner array
|
|
||||||
for i in 0 ..< outerLen:
|
|
||||||
result[i] = cast[ptr UncheckedArray[byte]](alloc0(sizeof(byte) * innerLen))
|
|
||||||
|
|
||||||
proc freeDoubleArray*(
|
|
||||||
arr: ptr UncheckedArray[ptr UncheckedArray[byte]], outerLen: int
|
|
||||||
) =
|
|
||||||
# Free each inner array
|
|
||||||
for i in 0 ..< outerLen:
|
|
||||||
if not arr[i].isNil:
|
|
||||||
dealloc(arr[i])
|
|
||||||
|
|
||||||
# Free outer array
|
|
||||||
if not arr.isNil:
|
|
||||||
dealloc(arr)
|
|
||||||
|
|
||||||
proc testPackets*(
|
proc testPackets*(
|
||||||
buffers,
|
buffers,
|
||||||
parity,
|
parity,
|
||||||
@ -96,37 +72,34 @@ proc testPackets*(
|
|||||||
decoder: var LeoDecoder): Result[void, cstring] =
|
decoder: var LeoDecoder): Result[void, cstring] =
|
||||||
|
|
||||||
var
|
var
|
||||||
dataBuf = createDoubleArray(buffers, bufSize)
|
dataBuf = newSeqOfCap[seq[byte]](buffers)
|
||||||
parityBuf = createDoubleArray(parity, bufSize)
|
parityBuf = newSeqOfCap[seq[byte]](parity)
|
||||||
recoveredBuf = createDoubleArray(buffers, bufSize)
|
recoveredBuf = newSeqOfCap[seq[byte]](buffers)
|
||||||
|
|
||||||
defer:
|
|
||||||
freeDoubleArray(dataBuf, buffers)
|
|
||||||
freeDoubleArray(parityBuf, parity)
|
|
||||||
freeDoubleArray(recoveredBuf, buffers)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for i in 0..<buffers:
|
for i in 0..<buffers:
|
||||||
var
|
var dataSeq = newSeq[byte](bufSize)
|
||||||
dataSeq = newSeq[byte](bufSize)
|
|
||||||
|
|
||||||
randomCRCPacket(dataSeq)
|
randomCRCPacket(dataSeq)
|
||||||
copyMem(dataBuf[i],addr dataSeq[0],bufSize)
|
dataBuf.add(dataSeq)
|
||||||
|
|
||||||
encoder.encode(dataBuf, parityBuf,buffers,parity).tryGet()
|
recoveredBuf.add(newSeq[byte](bufSize))
|
||||||
|
|
||||||
|
for _ in 0 ..< parity:
|
||||||
|
parityBuf.add(newSeq[byte](bufSize))
|
||||||
|
|
||||||
|
encoder.encode(dataBuf, parityBuf).tryGet()
|
||||||
|
|
||||||
if dataLosses > 0:
|
if dataLosses > 0:
|
||||||
dropRandomIdx(dataBuf,buffers, dataLosses)
|
dropRandomIdx(dataBuf, dataLosses)
|
||||||
|
|
||||||
if parityLosses > 0:
|
if parityLosses > 0:
|
||||||
dropRandomIdx(parityBuf,parity,parityLosses)
|
dropRandomIdx(parityBuf, parityLosses)
|
||||||
|
|
||||||
decoder.decode(dataBuf, parityBuf, recoveredBuf,buffers,parity,buffers).tryGet()
|
decoder.decode(dataBuf, parityBuf, recoveredBuf).tryGet()
|
||||||
|
|
||||||
for i in 0..<buffers:
|
for i, d in dataBuf:
|
||||||
if dataBuf[i].isNil:
|
if d.len <= 0:
|
||||||
if not checkCRCPacket(recoveredBuf[i],bufSize):
|
if not checkCRCPacket(recoveredBuf[i]):
|
||||||
return err(("Check failed for packet " & $i).cstring)
|
return err(("Check failed for packet " & $i).cstring)
|
||||||
|
|
||||||
ok()
|
ok()
|
||||||
|
|||||||
@ -18,86 +18,65 @@ suite "Leopard Parametrization":
|
|||||||
test "Should not allow invalid data/parity buffer counts":
|
test "Should not allow invalid data/parity buffer counts":
|
||||||
check:
|
check:
|
||||||
LeoEncoder.init(64, 1, 2).error ==
|
LeoEncoder.init(64, 1, 2).error ==
|
||||||
"number of parity buffers cannot exceed number of data buffers!"
|
"number of parity buffers cannot exceed number of data buffers!"
|
||||||
|
|
||||||
test "Should not allow data + parity to exceed 65536":
|
test "Should not allow data + parity to exceed 65536":
|
||||||
check:
|
check:
|
||||||
LeoEncoder.init(64, 65536 + 1, 0).error ==
|
LeoEncoder.init(64, 65536 + 1, 0).error ==
|
||||||
"number of parity and data buffers cannot exceed 65536!"
|
"number of parity and data buffers cannot exceed 65536!"
|
||||||
|
|
||||||
LeoEncoder.init(64, 32768 + 1, 32768).error ==
|
LeoEncoder.init(64, 32768 + 1, 32768).error ==
|
||||||
"number of parity and data buffers cannot exceed 65536!"
|
"number of parity and data buffers cannot exceed 65536!"
|
||||||
|
|
||||||
test "Should not allow encoding with invalid data buffer counts":
|
test "Should not allow encoding with invalid data buffer counts":
|
||||||
var
|
var
|
||||||
dataLen =3
|
|
||||||
parityLen = 2
|
|
||||||
leo = LeoEncoder.init(64, 4, 2).tryGet()
|
leo = LeoEncoder.init(64, 4, 2).tryGet()
|
||||||
data = createDoubleArray(dataLen, 64)
|
data = newSeq[seq[byte]](3)
|
||||||
parity = createDoubleArray(parityLen, 64)
|
parity = newSeq[seq[byte]](2)
|
||||||
defer:
|
|
||||||
freeDoubleArray(data, dataLen)
|
|
||||||
freeDoubleArray(parity, parityLen)
|
|
||||||
check:
|
check:
|
||||||
leo.encode(data, parity,dataLen,parityLen).error == "Number of data buffers should match!"
|
leo.encode(data, parity).error == "Number of data buffers should match!"
|
||||||
|
|
||||||
test "Should not allow encoding with invalid parity buffer counts":
|
test "Should not allow encoding with invalid parity buffer counts":
|
||||||
var
|
var
|
||||||
dataLen =4
|
|
||||||
parityLen = 3
|
|
||||||
leo = LeoEncoder.init(64, 4, 2).tryGet()
|
leo = LeoEncoder.init(64, 4, 2).tryGet()
|
||||||
data = createDoubleArray(dataLen, 64)
|
data = newSeq[seq[byte]](4)
|
||||||
parity = createDoubleArray(parityLen, 64)
|
parity = newSeq[seq[byte]](3)
|
||||||
|
|
||||||
defer:
|
|
||||||
freeDoubleArray(data, dataLen)
|
|
||||||
freeDoubleArray(parity, parityLen)
|
|
||||||
|
|
||||||
check:
|
check:
|
||||||
leo.encode(data, parity,dataLen,parityLen).error == "Number of parity buffers should match!"
|
leo.encode(data, parity).error == "Number of parity buffers should match!"
|
||||||
|
|
||||||
test "Should not allow decoding with invalid data buffer counts":
|
test "Should not allow decoding with invalid data buffer counts":
|
||||||
var
|
var
|
||||||
dataLen =3
|
|
||||||
parityLen = 2
|
|
||||||
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
||||||
data = createDoubleArray(dataLen, 64)
|
data = newSeq[seq[byte]](3)
|
||||||
parity = createDoubleArray(parityLen, 64)
|
parity = newSeq[seq[byte]](2)
|
||||||
recovered = createDoubleArray(dataLen, 64)
|
recovered = newSeq[seq[byte]](3)
|
||||||
|
|
||||||
defer:
|
|
||||||
freeDoubleArray(data, dataLen)
|
|
||||||
freeDoubleArray(parity, parityLen)
|
|
||||||
freeDoubleArray(recovered, dataLen)
|
|
||||||
|
|
||||||
check:
|
check:
|
||||||
leo.decode(data, parity, recovered,dataLen,parityLen,dataLen).error == "Number of data buffers should match!"
|
leo.decode(data, parity, recovered).error == "Number of data buffers should match!"
|
||||||
|
|
||||||
test "Should not allow decoding with invalid data buffer counts":
|
test "Should not allow decoding with invalid data buffer counts":
|
||||||
var
|
var
|
||||||
dataLen =4
|
|
||||||
parityLen = 1
|
|
||||||
recoveredLen = 3
|
|
||||||
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
||||||
data = createDoubleArray(dataLen, 64)
|
data = newSeq[seq[byte]](4)
|
||||||
parity = createDoubleArray(parityLen, 64)
|
parity = newSeq[seq[byte]](1)
|
||||||
recovered = createDoubleArray(recoveredLen, 64)
|
recovered = newSeq[seq[byte]](3)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
leo.decode(data, parity, recovered,dataLen,parityLen,recoveredLen).error == "Number of parity buffers should match!"
|
leo.decode(data, parity, recovered).error ==
|
||||||
|
"Number of parity buffers should match!"
|
||||||
|
|
||||||
test "Should not allow decoding with invalid data buffer counts":
|
test "Should not allow decoding with invalid data buffer counts":
|
||||||
var
|
var
|
||||||
dataLen =4
|
|
||||||
parityLen = 2
|
|
||||||
recoveredLen = 3
|
|
||||||
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
||||||
data = createDoubleArray(dataLen, 64)
|
data = newSeq[seq[byte]](4)
|
||||||
parity = createDoubleArray(parityLen, 64)
|
parity = newSeq[seq[byte]](2)
|
||||||
recovered = createDoubleArray(recoveredLen, 64)
|
recovered = newSeq[seq[byte]](3)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
leo.decode(data, parity, recovered,dataLen,parityLen,recoveredLen).error == "Number of recovered buffers should match buffers!"
|
leo.decode(data, parity, recovered).error ==
|
||||||
|
"Number of recovered buffers should match buffers!"
|
||||||
|
|
||||||
suite "Leopard simple Encode/Decode":
|
suite "Leopard simple Encode/Decode":
|
||||||
const
|
const
|
||||||
@ -109,76 +88,67 @@ suite "Leopard simple Encode/Decode":
|
|||||||
var
|
var
|
||||||
encoder: LeoEncoder
|
encoder: LeoEncoder
|
||||||
decoder: LeoDecoder
|
decoder: LeoDecoder
|
||||||
data: ptr UncheckedArray[ptr UncheckedArray[byte]]
|
data: seq[seq[byte]]
|
||||||
parity: ptr UncheckedArray[ptr UncheckedArray[byte]]
|
parity: seq[seq[byte]]
|
||||||
recovered: ptr UncheckedArray[ptr UncheckedArray[byte]]
|
recovered: seq[seq[byte]]
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
encoder = LeoEncoder.init(BufferSize, DataCount, ParityCount).tryGet()
|
encoder = LeoEncoder.init(BufferSize, DataCount, ParityCount).tryGet()
|
||||||
decoder = LeoDecoder.init(BufferSize, DataCount, ParityCount).tryGet()
|
decoder = LeoDecoder.init(BufferSize, DataCount, ParityCount).tryGet()
|
||||||
data = createDoubleArray(DataCount, BufferSize)
|
data = newSeq[seq[byte]](DataCount)
|
||||||
parity = createDoubleArray(ParityCount, BufferSize)
|
parity = newSeq[seq[byte]](ParityCount)
|
||||||
recovered = createDoubleArray(DataCount, BufferSize)
|
recovered = newSeq[seq[byte]](DataCount)
|
||||||
|
|
||||||
teardown:
|
teardown:
|
||||||
freeDoubleArray(data, DataCount)
|
|
||||||
freeDoubleArray(parity, ParityCount)
|
|
||||||
freeDoubleArray(recovered, DataCount)
|
|
||||||
encoder.free()
|
encoder.free()
|
||||||
decoder.free()
|
decoder.free()
|
||||||
|
|
||||||
test "Test 2 data loses out of 4 possible":
|
test "Test 2 data loses out of 4 possible":
|
||||||
for i in 0..<DataCount:
|
for i in 0 ..< DataCount:
|
||||||
var
|
data[i] = newSeq[byte](BufferSize)
|
||||||
str = TestString & " " & $i
|
recovered[i] = newSeq[byte](BufferSize)
|
||||||
|
var str = TestString & " " & $i
|
||||||
copyMem(data[i], addr str[0], str.len)
|
|
||||||
|
|
||||||
|
|
||||||
encoder.encode(data, parity,DataCount,ParityCount).tryGet()
|
|
||||||
|
|
||||||
var
|
|
||||||
data1 =cast[ptr UncheckedArray[byte]](allocShared0(sizeof(byte) * BufferSize))
|
|
||||||
data2 = cast[ptr UncheckedArray[byte]](allocShared0(sizeof(byte) * BufferSize))
|
|
||||||
|
|
||||||
defer:
|
|
||||||
deallocShared(data1)
|
|
||||||
deallocShared(data2)
|
|
||||||
|
|
||||||
copyMem(data1,data[0], BufferSize)
|
|
||||||
copyMem(data2,data[1], BufferSize)
|
|
||||||
|
|
||||||
data[0]=nil
|
|
||||||
data[1]=nil
|
|
||||||
|
|
||||||
decoder.decode(data, parity, recovered,DataCount,ParityCount,DataCount).tryGet()
|
|
||||||
|
|
||||||
check equalMem(recovered[0], data1, BufferSize)
|
|
||||||
check equalMem(recovered[1], data2, BufferSize)
|
|
||||||
|
|
||||||
test "Test 1 data and 1 parity loss out of 4 possible":
|
|
||||||
for i in 0..<DataCount:
|
|
||||||
var
|
|
||||||
str = TestString & " " & $i
|
|
||||||
|
|
||||||
copyMem(addr data[i][0], addr str[0], str.len)
|
copyMem(addr data[i][0], addr str[0], str.len)
|
||||||
|
|
||||||
encoder.encode(data, parity,DataCount,ParityCount).tryGet()
|
for i in 0 ..< ParityCount:
|
||||||
|
parity[i] = newSeq[byte](BufferSize)
|
||||||
|
|
||||||
|
encoder.encode(data, parity).tryGet()
|
||||||
var data1 = cast[ptr UncheckedArray[byte]](allocShared0(sizeof(byte) * BufferSize))
|
|
||||||
|
|
||||||
defer: deallocShared(data1)
|
var
|
||||||
|
data1 = data[0]
|
||||||
|
data2 = data[1]
|
||||||
|
|
||||||
copyMem(data1,data[0], BufferSize)
|
data[0].setLen(0)
|
||||||
|
data[1].setLen(0)
|
||||||
|
|
||||||
data[0]=nil
|
decoder.decode(data, parity, recovered).tryGet()
|
||||||
parity[0]=nil
|
|
||||||
|
|
||||||
decoder.decode(data, parity, recovered,DataCount,ParityCount,DataCount).tryGet()
|
check recovered[0] == data1
|
||||||
|
check recovered[1] == data2
|
||||||
|
|
||||||
check equalMem(recovered[0], data1, BufferSize)
|
test "Test 1 data and 1 parity loss out of 4 possible":
|
||||||
|
for i in 0 ..< DataCount:
|
||||||
|
data[i] = newSeq[byte](BufferSize)
|
||||||
|
recovered[i] = newSeq[byte](BufferSize)
|
||||||
|
|
||||||
|
var str = TestString & " " & $i
|
||||||
|
|
||||||
|
copyMem(addr data[i][0], addr str[0], str.len)
|
||||||
|
|
||||||
|
for i in 0 ..< ParityCount:
|
||||||
|
parity[i] = newSeq[byte](BufferSize)
|
||||||
|
|
||||||
|
encoder.encode(data, parity).tryGet()
|
||||||
|
|
||||||
|
var data1 = data[0]
|
||||||
|
|
||||||
|
data[0].setLen(0)
|
||||||
|
parity[0].setLen(0)
|
||||||
|
|
||||||
|
decoder.decode(data, parity, recovered).tryGet()
|
||||||
|
check recovered[0] == data1
|
||||||
|
|
||||||
suite "Leopard Encode/Decode":
|
suite "Leopard Encode/Decode":
|
||||||
test "bufSize = 4096, K = 800, M = 200 - drop data = 200 data":
|
test "bufSize = 4096, K = 800, M = 200 - drop data = 200 data":
|
||||||
@ -228,7 +198,8 @@ suite "Leopard Encode/Decode":
|
|||||||
try:
|
try:
|
||||||
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
||||||
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
||||||
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
|
||||||
|
.tryGet()
|
||||||
finally:
|
finally:
|
||||||
encoder.free()
|
encoder.free()
|
||||||
decoder.free()
|
decoder.free()
|
||||||
@ -246,7 +217,8 @@ suite "Leopard Encode/Decode":
|
|||||||
try:
|
try:
|
||||||
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
||||||
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
||||||
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
|
||||||
|
.tryGet()
|
||||||
finally:
|
finally:
|
||||||
encoder.free()
|
encoder.free()
|
||||||
decoder.free()
|
decoder.free()
|
||||||
@ -264,7 +236,8 @@ suite "Leopard Encode/Decode":
|
|||||||
try:
|
try:
|
||||||
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
||||||
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
||||||
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
|
||||||
|
.tryGet()
|
||||||
finally:
|
finally:
|
||||||
encoder.free()
|
encoder.free()
|
||||||
decoder.free()
|
decoder.free()
|
||||||
@ -282,7 +255,8 @@ suite "Leopard Encode/Decode":
|
|||||||
try:
|
try:
|
||||||
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
||||||
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
||||||
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
|
||||||
|
.tryGet()
|
||||||
finally:
|
finally:
|
||||||
encoder.free()
|
encoder.free()
|
||||||
decoder.free()
|
decoder.free()
|
||||||
@ -300,7 +274,8 @@ suite "Leopard Encode/Decode":
|
|||||||
try:
|
try:
|
||||||
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
||||||
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
||||||
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
|
||||||
|
.tryGet()
|
||||||
finally:
|
finally:
|
||||||
encoder.free()
|
encoder.free()
|
||||||
decoder.free()
|
decoder.free()
|
||||||
@ -318,7 +293,8 @@ suite "Leopard Encode/Decode":
|
|||||||
try:
|
try:
|
||||||
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
||||||
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
||||||
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
|
||||||
|
.tryGet()
|
||||||
finally:
|
finally:
|
||||||
encoder.free()
|
encoder.free()
|
||||||
decoder.free()
|
decoder.free()
|
||||||
@ -336,23 +312,25 @@ suite "Leopard Encode/Decode":
|
|||||||
try:
|
try:
|
||||||
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
||||||
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
||||||
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
|
||||||
|
.tryGet()
|
||||||
finally:
|
finally:
|
||||||
encoder.free()
|
encoder.free()
|
||||||
decoder.free()
|
decoder.free()
|
||||||
|
|
||||||
suite "Leopard use same encoder/decoder multiple times":
|
suite "Leopard use same encoder/decoder multiple times":
|
||||||
var
|
var
|
||||||
encoder: LeoEncoder
|
encoder: LeoEncoder
|
||||||
decoder: LeoDecoder
|
decoder: LeoDecoder
|
||||||
|
|
||||||
try:
|
try:
|
||||||
encoder = LeoEncoder.init(4096, 800, 800).tryGet()
|
encoder = LeoEncoder.init(4096, 800, 800).tryGet()
|
||||||
decoder = LeoDecoder.init(4096, 800, 800).tryGet()
|
decoder = LeoDecoder.init(4096, 800, 800).tryGet()
|
||||||
for i in 0..10:
|
for i in 0 .. 10:
|
||||||
let lost = 40 * i
|
let lost = 40 * i
|
||||||
test "Encode/Decode using same encoder/decoder - lost data = " & $lost & " lost parity = " & $lost:
|
test "Encode/Decode using same encoder/decoder - lost data = " & $lost &
|
||||||
testPackets(800, 800, 4096, 40 * i, 40 * i, encoder, decoder).tryGet()
|
" lost parity = " & $lost:
|
||||||
finally:
|
testPackets(800, 800, 4096, 40 * i, 40 * i, encoder, decoder).tryGet()
|
||||||
encoder.free()
|
finally:
|
||||||
decoder.free()
|
encoder.free()
|
||||||
|
decoder.free()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user