Compare commits

..

No commits in common. "main" and "0.1.0" have entirely different histories.
main ... 0.1.0

13 changed files with 303 additions and 230 deletions

View File

@ -1,45 +1,161 @@
name: Tests name: Tests
on: [push, pull_request, workflow_dispatch] on: [pull_request, push]
jobs: jobs:
test: tests:
env:
NPROC: 2
strategy: strategy:
fail-fast: false
matrix: matrix:
cache_nonce: [ 1 ]
nim_version: [ 1.2.18, 1.4.8, 1.6.6 ]
platform: platform:
- { - {
icon: 🐧, icon: 🐧,
label: Linux, label: Linux,
runner: ubuntu-latest os: ubuntu,
shell: bash --noprofile --norc -eo pipefail
} }
- { - {
icon: 🍎, icon: 🍎,
label: macOS, label: macOS,
runner: macos-latest os: macos,
shell: bash --noprofile --norc -eo pipefail
} }
- { - {
icon: 🏁, icon: 🏁,
label: Windows, label: Windows,
runner: windows-latest os: windows,
shell: msys2
} }
# Earliest supported and latest nim name: ${{ matrix.platform.icon }} ${{ matrix.platform.label }} - Nim v${{ matrix.nim_version }}
nim: [1.6.18, "stable"] runs-on: ${{ matrix.platform.os }}-latest
name: ${{ matrix.platform.icon }} ${{ matrix.platform.label }} - Nim v${{ matrix.nim }}
runs-on: ${{ matrix.platform.runner }}
defaults: defaults:
run: run:
shell: bash shell: ${{ matrix.platform.shell }} {0}
steps: steps:
- uses: actions/checkout@v4 # - name: Install tools and libraries via APT (Linux)
with: # if: matrix.platform.os == 'ubuntu'
submodules: recursive # run: |
- uses: jiro4989/setup-nim-action@v2 # sudo apt update
with: # sudo apt install -y \
nim-version: ${{matrix.nim}} # ...
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install - name: Install tools and libraries via Homebrew (macOS)
run: nimble install -y if: matrix.platform.os == 'macos'
- name: Build and run tests run: |
run: | brew update
eval nimble --verbose test -d:release --mm:refc brew install \
eval nimble --verbose test -d:release --mm:orc findutils \
libomp
- name: Install tools and libraries via MSYS2 (Windows)
if: matrix.platform.os == 'windows'
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
install: >
base-devel
git
mingw-w64-ucrt-x86_64-cmake
mingw-w64-ucrt-x86_64-toolchain
- name: Checkout sources from GitHub
uses: actions/checkout@v2
with:
submodules: true
- name: Calculate cache member paths
id: calc-paths
run: |
if [[ ${{ matrix.platform.os }} = windows ]]; then
echo "::set-output name=bash_env::$(cygpath -m "${HOME}")/.bash_env"
echo "::set-output name=choosenim::$(cygpath -m "${USERPROFILE}")/.choosenim"
echo "::set-output name=nimble::$(cygpath -m "${HOME}")/.nimble"
else
echo "::set-output name=bash_env::${HOME}/.bash_env"
echo "::set-output name=choosenim::${HOME}/.choosenim"
echo "::set-output name=nimble::${HOME}/.nimble"
fi
- name: Restore choosenim and Nim tooling from cache
id: choosenim-nim-tooling-cache
uses: actions/cache@v2
with:
path: |
${{ steps.calc-paths.outputs.bash_env }}
${{ steps.calc-paths.outputs.choosenim }}
${{ steps.calc-paths.outputs.nimble }}/bin
key: ${{ matrix.platform.os }}-nim_version:${{ matrix.nim_version }}-cache_nonce:${{ matrix.cache_nonce }}
- name: Install choosenim and Nim tooling
if: steps.choosenim-nim-tooling-cache.outputs.cache-hit != 'true'
run: |
mkdir -p "${HOME}/Downloads"
cd "${HOME}/Downloads"
curl https://nim-lang.org/choosenim/init.sh -sSf -O
chmod +x init.sh
if [[ ${{ matrix.platform.os }} = windows ]]; then
mkdir -p "$(cygpath "${USERPROFILE}")/.nimble/bin"
fi
CHOOSENIM_CHOOSE_VERSION=${{ matrix.nim_version }} ./init.sh -y
if [[ ${{ matrix.platform.os }} = windows ]]; then
mv "$(cygpath "${USERPROFILE}")/.nimble" "${HOME}/"
# intention is to rely only on libs provided by the OS and MSYS2 env
rm -rf "${HOME}/.nimble/bin/"*.dll
rm -rf "${HOME}/.nimble/bin/"*.pem
fi
echo 'export NIMBLE_DIR="${HOME}/.nimble"' >> "${HOME}/.bash_env"
echo 'export PATH="${NIMBLE_DIR}/bin:${PATH}"' >> "${HOME}/.bash_env"
- name: Install project dependencies
run: |
source "${HOME}/.bash_env"
cd "${NIMBLE_DIR}/bin"
# delete broken symlinks, which can arise because e.g. the cache
# restored a symlink that points to an executable within
# ../pkgs/foo-1.2.3/ but the project's .nimble file has been updated
# to install foo-#head; in the case of a broken symlink, nimble's
# auto-overwrite fails
if [[ ${{ matrix.platform.os }} = macos ]]; then
gfind . -xtype l -delete
else
find . -xtype l -delete
fi
cd -
nimble --accept install
- name: Build and run tests
run: |
source "${HOME}/.bash_env"
if [[ ${{ matrix.platform.os }} = windows ]]; then
touch tests/testleopard.exe
else
touch tests/testleopard
fi
if [[ ${{ matrix.platform.os }} = macos ]]; then
export PATH="$(brew --prefix llvm@14)/bin:${PATH}"
export LDFLAGS="-L$(brew --prefix libomp)/lib -L$(brew --prefix llvm@14)/lib -Wl,-rpath,$(brew --prefix llvm@14)/lib"
compiler_extra_options="-d:LeopardCmakeFlags='-DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=$(brew --prefix llvm@14)/bin/clang -DCMAKE_CXX_COMPILER=$(brew --prefix llvm@14)/bin/clang++' -d:LeopardExtraCompilerlags='-fopenmp' -d:LeopardExtraLinkerFlags='-fopenmp -L$(brew --prefix libomp)/lib'"
fi
if [[ ${{ matrix.nim_version }} = 1.2.* ]]; then
eval nimble --verbose test -d:release --gc:refc ${compiler_extra_options}
elif [[ ${{ matrix.nim_version }} = 1.4.* ]]; then
eval nimble --verbose test -d:release --gc:refc ${compiler_extra_options}
eval nimble --verbose test -d:release --gc:orc ${compiler_extra_options}
else
eval nimble --verbose test -d:release --mm:refc ${compiler_extra_options}
eval nimble --verbose test -d:release --mm:orc ${compiler_extra_options}
fi
if [[ ${{ matrix.platform.os }} = macos ]]; then
echo
echo otool -L tests/testleopard
otool -L tests/testleopard
else
echo
echo ldd tests/testleopard
ldd tests/testleopard
fi

1
.gitignore vendored
View File

@ -14,4 +14,3 @@ TODO
nimble.develop nimble.develop
nimble.paths nimble.paths
nimbledeps

View File

@ -10,7 +10,7 @@ Nim wrapper for [Leopard-RS](https://github.com/catid/leopard): a fast library f
## Requirements ## Requirements
* Same as Leopard-RS' requirements, e.g. CMake 3.7 or newer. * Same as Leopard-RS' requirements, e.g. CMake 3.7 or newer.
* Nim 1.6 or newer. * Nim 1.2 or newer.
## Installation ## Installation
@ -70,7 +70,7 @@ So, on macOS, when running `nimble test` of nim-leopard or compiling a project t
``` ```
and these flags should be passed to `nim c` and these flags should be passed to `nim c`
```text ```text
-d:LeopardCmakeFlags="-DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=$(brew --prefix)/opt/llvm/bin/clang -DCMAKE_CXX_COMPILER=$(brew --prefix)/opt/llvm/bin/clang++" -d:LeopardExtraCompilerFlags="-fopenmp" -d:LeopardExtraLinkerFlags="-fopenmp -L$(brew --prefix)/opt/libomp/lib" -d:LeopardCmakeFlags="-DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=$(brew --prefix)/opt/llvm/bin/clang -DCMAKE_CXX_COMPILER=$(brew --prefix)/opt/llvm/bin/clang++" -d:LeopardExtraCompilerlags="-fopenmp" -d:LeopardExtraLinkerFlags="-fopenmp -L$(brew --prefix)/opt/libomp/lib"
``` ```
## Usage ## Usage

View File

@ -1,9 +1,6 @@
--styleCheck:usages
--styleCheck:error
--threads:on --threads:on
--tlsEmulation:off --tlsEmulation:off
# begin Nimble config (version 2) # begin Nimble config (version 1)
--noNimblePath when fileExists("nimble.paths"):
when withDir(thisDir(), system.fileExists("nimble.paths")):
include "nimble.paths" include "nimble.paths"
# end Nimble config # end Nimble config

View File

@ -1,12 +1,13 @@
mode = ScriptMode.Verbose mode = ScriptMode.Verbose
packageName = "leopard" packageName = "leopard"
version = "0.1.1" version = "0.1.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"
installDirs = @["vendor"] installDirs = @["vendor"]
requires "nim >= 1.6.0", requires "nim >= 1.2.0",
"stew",
"unittest2", "unittest2",
"results" "upraises >= 0.1.0 & < 0.2.0"

View File

@ -7,9 +7,12 @@
## This file may not be copied, modified, or distributed except according to ## This file may not be copied, modified, or distributed except according to
## those terms. ## those terms.
{.push raises: [].} import pkg/upraises
push: {.upraises: [].}
import pkg/results {.deadCodeElim: on.}
import pkg/stew/results
import ./wrapper import ./wrapper
import ./utils import ./utils
@ -45,8 +48,8 @@ type
func encode*( func encode*(
self: var LeoEncoder, self: var LeoEncoder,
data,parity: ptr UncheckedArray[ptr UncheckedArray[byte]], data,
dataLen,parityLen: int ): Result[void, cstring] = parity: var openArray[seq[byte]]): 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,10 +57,10 @@ 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
@ -65,7 +68,7 @@ func encode*(
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
@ -74,14 +77,14 @@ func encode*(
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[ptr pointer](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()
@ -89,8 +92,7 @@ func decode*(
self: var LeoDecoder, self: var LeoDecoder,
data, data,
parity, parity,
recovered: ptr UncheckedArray[ptr UncheckedArray[byte]], recovered: var openArray[seq[byte]]): Result[void, cstring] =
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,13 +102,13 @@ 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
@ -116,38 +118,38 @@ func decode*(
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 = leo_decode(
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[ptr pointer](addr dataPtr[0]),
cast[LeoDataPtr](addr parityPtr[0]), cast[ptr pointer](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):
@ -216,7 +218,7 @@ proc init[TT: Leo](
# concurrently instantiate the library twice, and # concurrently instantiate the library twice, and
# might end up with two distinct versions - not a big # might end up with two distinct versions - not a big
# deal but will defeat the purpose of this `once` block # deal but will defeat the purpose of this `once` block
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

View File

@ -7,7 +7,10 @@
## This file may not be copied, modified, or distributed except according to ## This file may not be copied, modified, or distributed except according to
## those terms. ## those terms.
{.push raises: [].} import pkg/upraises
push: {.upraises: [].}
{.deadCodeElim: on.}
import system/ansi_c import system/ansi_c

View File

@ -7,7 +7,10 @@
## This file may not be copied, modified, or distributed except according to ## This file may not be copied, modified, or distributed except according to
## those terms. ## those terms.
{.push raises: [].} import pkg/upraises
push: {.upraises: [].}
{.deadCodeElim: on.}
# From awr1: https://github.com/nim-lang/Nim/pull/11816/files # From awr1: https://github.com/nim-lang/Nim/pull/11816/files

View File

@ -57,7 +57,8 @@
## Conference on File and Storage Technologies, San Jose, 2013 ## Conference on File and Storage Technologies, San Jose, 2013
{.push raises: [].} import pkg/upraises
push: {.upraises: [].}
## ----------------------------------------------------------------------------- ## -----------------------------------------------------------------------------
## Build configuration ## Build configuration
@ -66,9 +67,6 @@ import std/compilesettings
import std/os import std/os
import std/strutils import std/strutils
type
LeoDataPtr* {.importc: "const void* const*", bycopy.} = pointer
const const
LeopardCmakeFlags {.strdefine.} = LeopardCmakeFlags {.strdefine.} =
when defined(macosx): when defined(macosx):
@ -146,8 +144,8 @@ static:
discard gorge "rm -rf " & buildDir discard gorge "rm -rf " & buildDir
raise (ref Defect)(msg: "Failed to build Leopard-RS") raise (ref Defect)(msg: "Failed to build Leopard-RS")
{.passc: LeopardCompilerFlags & " " & LeopardExtraCompilerFlags.} {.passC: LeopardCompilerFlags & " " & LeopardExtraCompilerFlags.}
{.passl: LeopardLinkerFlags & " " & LeopardExtraLinkerFlags.} {.passL: LeopardLinkerFlags & " " & LeopardExtraLinkerFlags.}
{.pragma: leo, cdecl, header: LeopardHeader.} {.pragma: leo, cdecl, header: LeopardHeader.}
@ -227,7 +225,7 @@ proc leoEncode*(
originalCount: cuint; originalCount: cuint;
recoveryCount: cuint; recoveryCount: cuint;
workCount: cuint; workCount: cuint;
originalData: LeoDataPtr; originalData: ptr pointer;
workData: ptr pointer): LeopardResult {.leo, importc: "leo_encode".} workData: ptr pointer): LeopardResult {.leo, importc: "leo_encode".}
## Number of bytes in each data buffer ## Number of bytes in each data buffer
## Number of original_data[] buffer pointers ## Number of original_data[] buffer pointers
@ -242,7 +240,7 @@ proc leoEncode*(
## ##
## leo_decode_work_count() ## leo_decode_work_count()
## ##
## Calculate the number of work_data buffers to provide to leoDecode(). ## Calculate the number of work_data buffers to provide to leo_decode().
## ##
## The sum of original_count + recovery_count must not exceed 65536. ## The sum of original_count + recovery_count must not exceed 65536.
## ##
@ -253,7 +251,7 @@ proc leoEncode*(
proc leoDecodeWorkCount*(originalCount: cuint; recoveryCount: cuint): cuint proc leoDecodeWorkCount*(originalCount: cuint; recoveryCount: cuint): cuint
{.leo, importc: "leo_decode_work_count".} {.leo, importc: "leo_decode_work_count".}
## ##
## leoDecode() ## leo_decode()
## ##
## Decode original data from recovery data. ## Decode original data from recovery data.
## ##
@ -279,8 +277,8 @@ proc leoDecode*(
originalCount: cuint; originalCount: cuint;
recoveryCount: cuint; recoveryCount: cuint;
workCount: cuint; workCount: cuint;
originalData: LeoDataPtr; originalData: ptr pointer;
recoveryData: LeoDataPtr; recoveryData: ptr pointer;
workData: ptr pointer): LeopardResult {.leo, importc: "leo_decode".} workData: ptr pointer): LeopardResult {.leo, importc: "leo_decode".}
## Number of bytes in each data buffer ## Number of bytes in each data buffer
## Number of original_data[] buffer pointers ## Number of original_data[] buffer pointers

View File

@ -1,26 +1,35 @@
{ {
"version": 2, "version": 1,
"packages": { "packages": {
"unittest2": { "unittest2": {
"version": "0.2.5", "version": "0.0.4",
"vcsRevision": "26f2ef3ae0ec72a2a75bfe557e02e88f6a31c189", "vcsRevision": "f180f596c88dfd266f746ed6f8dbebce39c824db",
"url": "https://github.com/status-im/nim-unittest2", "url": "https://github.com/status-im/nim-unittest2.git",
"downloadMethod": "git", "downloadMethod": "git",
"dependencies": [], "dependencies": [],
"checksums": { "checksums": {
"sha1": "02bb3751ba9ddc3c17bfd89f2e41cb6bfb8fc0c9" "sha1": "fa309c41eaf6ef57895b9e603f2620a2f6e11780"
} }
}, },
"results": { "upraises": {
"version": "0.5.1", "version": "0.1.0",
"vcsRevision": "df8113dda4c2d74d460a8fa98252b0b771bf1f27", "vcsRevision": "ff4f8108e44fba9b35cac535ab63d3927e8fd3c2",
"url": "https://github.com/arnetheduck/nim-results", "url": "https://github.com/markspanbroek/upraises.git",
"downloadMethod": "git", "downloadMethod": "git",
"dependencies": [], "dependencies": [],
"checksums": { "checksums": {
"sha1": "a9c011f74bc9ed5c91103917b9f382b12e82a9e7" "sha1": "a0243c8039e12d547dbb2e9c73789c16bb8bc956"
}
},
"stew": {
"version": "0.1.0",
"vcsRevision": "6ad35b876fb6ebe0dfee0f697af173acc47906ee",
"url": "https://github.com/status-im/nim-stew.git",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "46d58c4feb457f3241e3347778334e325dce5268"
} }
} }
}, }
"tasks": {}
} }

View File

@ -1,6 +1,6 @@
import std/random import std/random
import pkg/results import pkg/stew/results
import ../leopard import ../leopard
proc randomCRCPacket*(data: var openArray[byte]) = proc randomCRCPacket*(data: var openArray[byte]) =
@ -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,35 @@ 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: for _ in 0..<buffers:
freeDoubleArray(dataBuf, buffers)
freeDoubleArray(parityBuf, parity)
freeDoubleArray(recoveredBuf, 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()

View File

@ -2,7 +2,7 @@ import std/random
import std/sets import std/sets
import pkg/unittest2 import pkg/unittest2
import pkg/results import pkg/stew/results
import ../leopard import ../leopard
import ./helpers import ./helpers
@ -30,74 +30,51 @@ 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
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 +86,70 @@ 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)
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 var
str = TestString & " " & $i 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)) var
data1 = data[0]
data2 = data[1]
defer: deallocShared(data1) data[0].setLen(0)
data[1].setLen(0)
copyMem(data1,data[0], BufferSize) decoder.decode(data, parity, recovered).tryGet()
data[0]=nil check recovered[0] == data1
parity[0]=nil check recovered[1] == data2
decoder.decode(data, parity, recovered,DataCount,ParityCount,DataCount).tryGet() 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)
check equalMem(recovered[0], data1, 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":

2
vendor/leopard vendored

@ -1 +1 @@
Subproject commit 670c5e6ef8565e87cec005051fcd162b1b581add Subproject commit 20eb7c8bc5ba41349091addd4e374d4b74e9fb07