2022-03-28 18:42:45 -06:00
|
|
|
import std/random
|
|
|
|
|
import std/sets
|
|
|
|
|
|
|
|
|
|
import pkg/unittest2
|
2025-12-10 22:11:55 +01:00
|
|
|
import pkg/results
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
import ../leopard
|
|
|
|
|
import ./helpers
|
|
|
|
|
|
|
|
|
|
randomize()
|
|
|
|
|
|
|
|
|
|
suite "Leopard Parametrization":
|
|
|
|
|
test "Should not allow invalid buffer multiples":
|
|
|
|
|
check:
|
|
|
|
|
LeoEncoder.init(63, 4, 2).error == "bufSize should be multiples of 64 bytes!"
|
|
|
|
|
LeoEncoder.init(65, 4, 2).error == "bufSize should be multiples of 64 bytes!"
|
|
|
|
|
|
|
|
|
|
test "Should not allow invalid data/parity buffer counts":
|
|
|
|
|
check:
|
|
|
|
|
LeoEncoder.init(64, 1, 2).error ==
|
|
|
|
|
"number of parity buffers cannot exceed number of data buffers!"
|
|
|
|
|
|
|
|
|
|
test "Should not allow data + parity to exceed 65536":
|
|
|
|
|
check:
|
|
|
|
|
LeoEncoder.init(64, 65536 + 1, 0).error ==
|
|
|
|
|
"number of parity and data buffers cannot exceed 65536!"
|
|
|
|
|
|
|
|
|
|
LeoEncoder.init(64, 32768 + 1, 32768).error ==
|
|
|
|
|
"number of parity and data buffers cannot exceed 65536!"
|
|
|
|
|
|
|
|
|
|
test "Should not allow encoding with invalid data buffer counts":
|
|
|
|
|
var
|
2025-02-12 00:44:18 +05:30
|
|
|
dataLen =3
|
|
|
|
|
parityLen = 2
|
2022-03-28 18:42:45 -06:00
|
|
|
leo = LeoEncoder.init(64, 4, 2).tryGet()
|
2025-02-12 00:44:18 +05:30
|
|
|
data = createDoubleArray(dataLen, 64)
|
|
|
|
|
parity = createDoubleArray(parityLen, 64)
|
|
|
|
|
defer:
|
|
|
|
|
freeDoubleArray(data, dataLen)
|
|
|
|
|
freeDoubleArray(parity, parityLen)
|
2022-03-28 18:42:45 -06:00
|
|
|
check:
|
2025-02-12 00:44:18 +05:30
|
|
|
leo.encode(data, parity,dataLen,parityLen).error == "Number of data buffers should match!"
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
test "Should not allow encoding with invalid parity buffer counts":
|
|
|
|
|
var
|
2025-02-12 00:44:18 +05:30
|
|
|
dataLen =4
|
|
|
|
|
parityLen = 3
|
2022-03-28 18:42:45 -06:00
|
|
|
leo = LeoEncoder.init(64, 4, 2).tryGet()
|
2025-02-12 00:44:18 +05:30
|
|
|
data = createDoubleArray(dataLen, 64)
|
|
|
|
|
parity = createDoubleArray(parityLen, 64)
|
|
|
|
|
|
|
|
|
|
defer:
|
|
|
|
|
freeDoubleArray(data, dataLen)
|
|
|
|
|
freeDoubleArray(parity, parityLen)
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
check:
|
2025-02-12 00:44:18 +05:30
|
|
|
leo.encode(data, parity,dataLen,parityLen).error == "Number of parity buffers should match!"
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
test "Should not allow decoding with invalid data buffer counts":
|
|
|
|
|
var
|
2025-02-12 00:44:18 +05:30
|
|
|
dataLen =3
|
|
|
|
|
parityLen = 2
|
2022-03-28 18:42:45 -06:00
|
|
|
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
2025-02-12 00:44:18 +05:30
|
|
|
data = createDoubleArray(dataLen, 64)
|
|
|
|
|
parity = createDoubleArray(parityLen, 64)
|
|
|
|
|
recovered = createDoubleArray(dataLen, 64)
|
|
|
|
|
|
|
|
|
|
defer:
|
|
|
|
|
freeDoubleArray(data, dataLen)
|
|
|
|
|
freeDoubleArray(parity, parityLen)
|
|
|
|
|
freeDoubleArray(recovered, dataLen)
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
check:
|
2025-02-12 00:44:18 +05:30
|
|
|
leo.decode(data, parity, recovered,dataLen,parityLen,dataLen).error == "Number of data buffers should match!"
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
test "Should not allow decoding with invalid data buffer counts":
|
|
|
|
|
var
|
2025-02-12 00:44:18 +05:30
|
|
|
dataLen =4
|
|
|
|
|
parityLen = 1
|
|
|
|
|
recoveredLen = 3
|
2022-03-28 18:42:45 -06:00
|
|
|
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
2025-02-12 00:44:18 +05:30
|
|
|
data = createDoubleArray(dataLen, 64)
|
|
|
|
|
parity = createDoubleArray(parityLen, 64)
|
|
|
|
|
recovered = createDoubleArray(recoveredLen, 64)
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
check:
|
2025-02-12 00:44:18 +05:30
|
|
|
leo.decode(data, parity, recovered,dataLen,parityLen,recoveredLen).error == "Number of parity buffers should match!"
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
test "Should not allow decoding with invalid data buffer counts":
|
|
|
|
|
var
|
2025-02-12 00:44:18 +05:30
|
|
|
dataLen =4
|
|
|
|
|
parityLen = 2
|
|
|
|
|
recoveredLen = 3
|
2022-03-28 18:42:45 -06:00
|
|
|
leo = LeoDecoder.init(64, 4, 2).tryGet()
|
2025-02-12 00:44:18 +05:30
|
|
|
data = createDoubleArray(dataLen, 64)
|
|
|
|
|
parity = createDoubleArray(parityLen, 64)
|
|
|
|
|
recovered = createDoubleArray(recoveredLen, 64)
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
check:
|
2025-02-12 00:44:18 +05:30
|
|
|
leo.decode(data, parity, recovered,dataLen,parityLen,recoveredLen).error == "Number of recovered buffers should match buffers!"
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
suite "Leopard simple Encode/Decode":
|
|
|
|
|
const
|
|
|
|
|
TestString = "Hello World!"
|
|
|
|
|
DataCount = 4
|
|
|
|
|
ParityCount = 2
|
|
|
|
|
BufferSize = 64
|
|
|
|
|
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
2025-02-12 00:44:18 +05:30
|
|
|
data: ptr UncheckedArray[ptr UncheckedArray[byte]]
|
|
|
|
|
parity: ptr UncheckedArray[ptr UncheckedArray[byte]]
|
|
|
|
|
recovered: ptr UncheckedArray[ptr UncheckedArray[byte]]
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
setup:
|
|
|
|
|
encoder = LeoEncoder.init(BufferSize, DataCount, ParityCount).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(BufferSize, DataCount, ParityCount).tryGet()
|
2025-02-12 00:44:18 +05:30
|
|
|
data = createDoubleArray(DataCount, BufferSize)
|
|
|
|
|
parity = createDoubleArray(ParityCount, BufferSize)
|
|
|
|
|
recovered = createDoubleArray(DataCount, BufferSize)
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
teardown:
|
2025-02-12 00:44:18 +05:30
|
|
|
freeDoubleArray(data, DataCount)
|
|
|
|
|
freeDoubleArray(parity, ParityCount)
|
|
|
|
|
freeDoubleArray(recovered, DataCount)
|
2022-03-28 18:42:45 -06:00
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|
|
|
|
|
|
|
|
|
|
test "Test 2 data loses out of 4 possible":
|
|
|
|
|
for i in 0..<DataCount:
|
|
|
|
|
var
|
|
|
|
|
str = TestString & " " & $i
|
|
|
|
|
|
2025-02-12 00:44:18 +05:30
|
|
|
copyMem(data[i], addr str[0], str.len)
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
|
2025-02-12 00:44:18 +05:30
|
|
|
encoder.encode(data, parity,DataCount,ParityCount).tryGet()
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
var
|
2025-02-12 00:44:18 +05:30
|
|
|
data1 =cast[ptr UncheckedArray[byte]](allocShared0(sizeof(byte) * BufferSize))
|
|
|
|
|
data2 = cast[ptr UncheckedArray[byte]](allocShared0(sizeof(byte) * BufferSize))
|
2022-03-28 18:42:45 -06:00
|
|
|
|
2025-02-12 00:44:18 +05:30
|
|
|
defer:
|
|
|
|
|
deallocShared(data1)
|
|
|
|
|
deallocShared(data2)
|
|
|
|
|
|
|
|
|
|
copyMem(data1,data[0], BufferSize)
|
|
|
|
|
copyMem(data2,data[1], BufferSize)
|
|
|
|
|
|
|
|
|
|
data[0]=nil
|
|
|
|
|
data[1]=nil
|
2022-03-28 18:42:45 -06:00
|
|
|
|
2025-02-12 00:44:18 +05:30
|
|
|
decoder.decode(data, parity, recovered,DataCount,ParityCount,DataCount).tryGet()
|
2022-03-28 18:42:45 -06:00
|
|
|
|
2025-02-12 00:44:18 +05:30
|
|
|
check equalMem(recovered[0], data1, BufferSize)
|
|
|
|
|
check equalMem(recovered[1], data2, BufferSize)
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
2025-02-12 00:44:18 +05:30
|
|
|
encoder.encode(data, parity,DataCount,ParityCount).tryGet()
|
2022-03-28 18:42:45 -06:00
|
|
|
|
2025-02-12 00:44:18 +05:30
|
|
|
|
|
|
|
|
var data1 = cast[ptr UncheckedArray[byte]](allocShared0(sizeof(byte) * BufferSize))
|
2022-03-28 18:42:45 -06:00
|
|
|
|
2025-02-12 00:44:18 +05:30
|
|
|
defer: deallocShared(data1)
|
|
|
|
|
|
|
|
|
|
copyMem(data1,data[0], BufferSize)
|
|
|
|
|
|
|
|
|
|
data[0]=nil
|
|
|
|
|
parity[0]=nil
|
|
|
|
|
|
|
|
|
|
decoder.decode(data, parity, recovered,DataCount,ParityCount,DataCount).tryGet()
|
2022-03-28 18:42:45 -06:00
|
|
|
|
2025-02-12 00:44:18 +05:30
|
|
|
check equalMem(recovered[0], data1, BufferSize)
|
2022-03-28 18:42:45 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
suite "Leopard Encode/Decode":
|
|
|
|
|
test "bufSize = 4096, K = 800, M = 200 - drop data = 200 data":
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
|
|
|
|
buffers = 800
|
|
|
|
|
parity = 200
|
|
|
|
|
bufSize = 4096
|
|
|
|
|
dataLoses = 200
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
testPackets(buffers, parity, bufSize, dataLoses, 0, encoder, decoder).tryGet()
|
|
|
|
|
finally:
|
|
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|
|
|
|
|
|
|
|
|
|
test "bufSize = 4096, K = 800, M = 200 - drop parity = 200":
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
|
|
|
|
buffers = 800
|
|
|
|
|
parity = 200
|
|
|
|
|
bufSize = 4096
|
|
|
|
|
parityLoses = 200
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
testPackets(buffers, parity, bufSize, parityLoses, 0, encoder, decoder).tryGet()
|
|
|
|
|
finally:
|
|
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|
|
|
|
|
|
|
|
|
|
test "bufSize = 4096, K = 800, M = 200 - drop data = 100, drop parity = 100":
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
|
|
|
|
buffers = 800
|
|
|
|
|
parity = 200
|
|
|
|
|
bufSize = 4096
|
|
|
|
|
dataLoses = 100
|
|
|
|
|
parityLoses = 100
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
|
|
|
|
finally:
|
|
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|
|
|
|
|
|
|
|
|
|
test "bufSize = 4096, K = 8000, M = 2000 - drop data = 2000":
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
|
|
|
|
buffers = 8000
|
|
|
|
|
parity = 2000
|
|
|
|
|
bufSize = 4096
|
|
|
|
|
dataLoses = 2000
|
|
|
|
|
parityLoses = 0
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
|
|
|
|
finally:
|
|
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|
|
|
|
|
|
|
|
|
|
test "bufSize = 4096, K = 8000, M = 2000 - drop parity = 2000":
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
|
|
|
|
buffers = 8000
|
|
|
|
|
parity = 2000
|
|
|
|
|
bufSize = 4096
|
|
|
|
|
dataLoses = 0
|
|
|
|
|
parityLoses = 2000
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
|
|
|
|
finally:
|
|
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|
|
|
|
|
|
|
|
|
|
test "bufSize = 4096, K = 8000, M = 2000 - drop data = 1000, parity = 1000":
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
|
|
|
|
buffers = 8000
|
|
|
|
|
parity = 2000
|
|
|
|
|
bufSize = 4096
|
|
|
|
|
dataLoses = 1000
|
|
|
|
|
parityLoses = 1000
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
|
|
|
|
finally:
|
|
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|
|
|
|
|
|
|
|
|
|
test "bufSize = 4096, K = 8000, M = 8000 - drop data = 8000":
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
|
|
|
|
buffers = 8000
|
|
|
|
|
parity = 8000
|
|
|
|
|
bufSize = 4096
|
|
|
|
|
dataLoses = 8000
|
|
|
|
|
parityLoses = 0
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
|
|
|
|
finally:
|
|
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|
|
|
|
|
|
|
|
|
|
test "bufSize = 4096, K = 8000, M = 8000 - drop parity = 8000":
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
|
|
|
|
buffers = 8000
|
|
|
|
|
parity = 8000
|
|
|
|
|
bufSize = 4096
|
|
|
|
|
dataLoses = 0
|
|
|
|
|
parityLoses = 8000
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
|
|
|
|
finally:
|
|
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|
|
|
|
|
|
|
|
|
|
test "bufSize = 4096, K = 8000, M = 8000 - drop data = 4000, parity = 4000":
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
|
|
|
|
buffers = 8000
|
|
|
|
|
parity = 8000
|
|
|
|
|
bufSize = 4096
|
|
|
|
|
dataLoses = 4000
|
|
|
|
|
parityLoses = 4000
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
|
|
|
|
|
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder).tryGet()
|
|
|
|
|
finally:
|
|
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|
|
|
|
|
|
|
|
|
|
suite "Leopard use same encoder/decoder multiple times":
|
|
|
|
|
var
|
|
|
|
|
encoder: LeoEncoder
|
|
|
|
|
decoder: LeoDecoder
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
encoder = LeoEncoder.init(4096, 800, 800).tryGet()
|
|
|
|
|
decoder = LeoDecoder.init(4096, 800, 800).tryGet()
|
|
|
|
|
for i in 0..10:
|
|
|
|
|
let lost = 40 * i
|
|
|
|
|
test "Encode/Decode using same encoder/decoder - lost data = " & $lost & " lost parity = " & $lost:
|
|
|
|
|
testPackets(800, 800, 4096, 40 * i, 40 * i, encoder, decoder).tryGet()
|
|
|
|
|
finally:
|
|
|
|
|
encoder.free()
|
|
|
|
|
decoder.free()
|