import pkg/stew/ptrops import pkg/stew/results import pkg/upraises import ./leopard/wrapper export results push: {.upraises: [].} const LeopardBadCodeMsg = "Bad RS code" LeopardInconsistentSizeMsg = "Buffer sizes must all be the same multiple of 64 bytes" LeopardNeedLessDataMsg = "Too much recovery data received" LeopardNotEnoughDataMsg = "Buffer counts are too low" MinBufferSize* = 64.uint MinSymbols* = 1.uint MaxTotalSymbols* = 65536.uint type Data* = seq[seq[byte]] LeopardDefect* = object of Defect # It should not be necessary to redefine LeopardResult, but if that's not # done here then defining LeopardError as `object of CatchableError` will # cause a mystery crash at compile-time (symbol not found). Can workaround by # defining as just `object`, but then when trying to work with LeopardResult # errors in e.g. tests/test_leopard.nim the same mystery crash happens at # compile-time. The problem may be related to use of importcpp in # leopard/wrapper.nim, so it could be a compiler bug. By redefining # LeopardResult in this module (and casting wrapper.LeopardResult values) the # the problem is avoided. LeopardResult* = enum LeopardNotEnoughData = -11.cint # Buffer counts are too low LeopardNeedLessData = -10.cint # Too much recovery data received LeopardInconsistentSize = -9.cint # Buffer sizes must all be the same multiple of 64 bytes LeopardBadCode = -8.cint # Bad RS code LeopardCallInitialize = wrapper.LeopardCallInitialize LeopardPlatform = wrapper.LeopardPlatform LeopardInvalidInput = wrapper.LeopardInvalidInput LeopardInvalidCounts = wrapper.LeopardInvalidCounts LeopardInvalidSize = wrapper.LeopardInvalidSize LeopardTooMuchData = wrapper.LeopardTooMuchData LeopardNeedMoreData = wrapper.LeopardNeedMoreData LeopardSuccess = wrapper.LeopardSuccess LeopardError* = object of CatchableError code*: LeopardResult ParityData* = Data ReedSolomonCode* = tuple[codeword, data, parity: uint] # symbol counts # workaround for https://github.com/nim-lang/Nim/issues/19619 # necessary for use of nim-leopard in nimbus-build-system projects because nbs # ships libbacktrace by default proc `$`*(err: LeopardError): string {.noSideEffect.} = $err # https://github.com/catid/leopard/issues/12 # https://www.cs.cmu.edu/~guyb/realworld/reedsolomon/reed_solomon_codes.html # # RS(255,239) # --------------------------------- # codeword symbols = 255 # data symbols = 239 # parity symbols = 255 - 239 = 16 proc RS*(codeword, data: Positive): ReedSolomonCode = var parity = codeword - data if parity < 0: parity = 0 (codeword: codeword.uint, data: data.uint, parity: parity.uint) func isValid*(code: ReedSolomonCode): bool = not ((code.codeword - code.data != code.parity) or (code.parity > code.data) or (code.codeword < MinSymbols + 1) or (code.data < MinSymbols) or (code.parity < MinSymbols) or (code.codeword > MaxTotalSymbols)) proc leoInit*() = if wrapper.leoInit() != 0: raise (ref LeopardDefect)(msg: "Leopard-RS failed to initialize") proc encode*(code: ReedSolomonCode, data: Data): Result[ParityData, LeopardError] = if not code.isValid: return err LeopardError(code: LeopardBadCode, msg: LeopardBadCodeMsg) var data = data let symbolBytes = data[0].len if data.len < code.data.int: return err LeopardError(code: LeopardNotEnoughData, msg: LeopardNotEnoughDataMsg) elif data.len > code.data.int: return err LeopardError(code: LeopardTooMuchData, msg: $leoResultString(wrapper.LeopardTooMuchData)) if symbolBytes < MinBufferSize.int or symbolBytes mod MinBufferSize.int != 0: return err LeopardError(code: LeopardInvalidSize, msg: $leoResultString(wrapper.LeopardInvalidSize)) var enData = newSeq[pointer](code.data) for i in 0.. code.data.int: return err LeopardError(code: LeopardTooMuchData, msg: $leoResultString(wrapper.LeopardTooMuchData)) if parityData.len < code.parity.int: return err LeopardError(code: LeopardNeedMoreData, msg: $leoResultString(wrapper.LeopardNeedMoreData)) elif parityData.len > code.parity.int: return err LeopardError(code: LeopardNeedLessData, msg: LeopardNeedLessDataMsg) if symbolBytes < MinBufferSize or symbolBytes mod MinBufferSize != 0: return err LeopardError(code: LeopardInvalidSize, msg: $leoResultString(wrapper.LeopardInvalidSize)) var deData = newSeq[pointer](code.data) for i in 0..