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 # 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 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 RS*(codeword, data: Positive): ReedSolomonCode = var parity = codeword - data if parity < 0: parity = 0 (codeword: codeword.uint, data: data.uint, parity: parity.uint) when (NimMajor, NimMinor, NimPatch) < (1, 4, 0): const header = "" proc c_malloc(size: csize_t): pointer {.importc: "malloc", header: header.} proc c_free(p: pointer) {.importc: "free", header: header.} proc SIMDSafeAllocate(size: int): pointer {.inline.} = var data = when (NimMajor, NimMinor, NimPatch) < (1, 4, 0): c_malloc(LEO_ALIGN_BYTES + size.uint) else: allocShared(LEO_ALIGN_BYTES + size.uint) doffset = cast[uint](data) mod LEO_ALIGN_BYTES data = offset(data, (LEO_ALIGN_BYTES + doffset).int) var offsetPtr = cast[pointer](cast[uint](data) - 1) moveMem(offsetPtr, addr doffset, sizeof(doffset)) data proc SIMDSafeFree(data: pointer) {.inline.} = var data = data if not data.isNil: let offset = cast[uint](data) - 1 if offset >= LEO_ALIGN_BYTES: return data = cast[pointer](cast[uint](data) - (LEO_ALIGN_BYTES - offset)) when (NimMajor, NimMinor, NimPatch) < (1, 4, 0): c_free data else: deallocShared data 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..