Giter Club home page Giter Club logo

tomerfiliba-org / reedsolomon Goto Github PK

View Code? Open in Web Editor NEW
346.0 13.0 85.0 1.18 MB

⏳🛡 Pythonic universal errors-and-erasures Reed-Solomon codec to protect your data from errors and bitrot. Includes a future-proof zero-dependencies pure-python implementation 🔮 and an optional speed-optimized Cython/C extension 🚀

Home Page: http://pypi.python.org/pypi/reedsolo

License: Other

Python 20.44% Makefile 0.91% Cython 11.07% Jupyter Notebook 67.58%
cython error-correcting-codes error-correction error-correction-codes parity-check python python2 python3 reed-solomon reed-solomon-codes

reedsolomon's Introduction

Reed Solomon

PyPI-Status PyPI-Versions PyPI-Downloads

Build-Status Coverage

Conda-Forge-Status Conda-Forge-Platforms Conda-Forge-Downloads

⏳🛡 Pythonic universal errors-and-erasures Reed-Solomon encoder/decoder codec to protect your data from errors and bitrot. Includes a future-proof zero-dependencies pure-python implementation 🔮 as well as an optional speed-optimized Cython/C extension 🚀

This is a burst-type implementation, so that it supports any Galois field higher than 2^3, but not binary streams. Burst errors are non-random errors that more often happen on data storage mediums such as hard drives, hence this library is better suited for data storage protection, and less for streams noise correction, although it also works for this purpose but with a bit of overhead (since it works with bytes only, instead of bits).

Based on the wonderful tutorial at Wikiversity, written by "Bobmath" and "LRQ3000". If you are just starting with Reed-Solomon error correction codes, the Wikiversity article is a good beginner's introduction.


Table of contents

Installation

For the latest stable release (compatible with Python >= 3.7), install with:

pip install --upgrade reedsolo

For the latest development release, use:

pip install --upgrade reedsolo --pre

For the cutting-edge code (likely unstable, do not use in production!), use:

pip install --upgrade git+https://github.com/tomerfiliba-org/reedsolomon

If you have some issues installing through pip, maybe this command may help, by forcing the use of sdist instead of wheels:

pip install reedsolo --no-binary="reedsolo" --no-cache

Note: for Python 2.7 and Python <= 3.6, please use v1.7.0:

pip install --upgrade reedsolo==1.7.0

Through wheels/pip, a pure-python implementation called reedsolo is installed, and for platforms supported by cibuildwheel, a precompiled speed-optimized creedsolo module is included. For other platforms or to compile from source (this requires cython>=3.0.0b2 and a C compiler), a build option can be specified:

# To compile from the latest stable release:
pip install --upgrade reedsolo --no-binary "reedsolo" --no-cache --config-setting="--build-option=--cythonize" --use-pep517 --isolated --verbose
# To compile from the latest development release:
pip install --upgrade reedsolo --no-binary "reedsolo" --no-cache --config-setting="--build-option=--cythonize" --use-pep517 --isolated --pre --verbose
# To compile from the cutting edge code:
pip install --upgrade "reedsolo @ git+https://github.com/tomerfiliba-org/reedsolomon" --no-binary "reedsolo" --no-cache --config-setting="--build-option=--cythonize" --use-pep517 --isolated --pre --verbose

The --config-setting="--build-option=--cythonize" flag signals to the setuptools backend to propagate to reedsolo's setup.py to build the optional cythonized extension. The --no-binary "reedsolo" and --no-cache options are necessary since pip 23.1 to force the use of the sdist instead of wheels.

or locally with:

pip install --upgrade . --config-setting="--build-option=--cythonize" --verbose

Note: for development, it's possible to add the --editable flag to use the local folder without installing in site-packages, and use .[test] instead of . to install all required packages to test this module locally.

The package for the development or cutting-edge releases can also be built locally with the pep517 compliant build tool:

pip install build
# With cythonization (from *.pyx to *.c to *.pyd)
python -sBm build --config-setting="--build-option=--cythonize"
# or skip cythonization and only compile from the already transpiled c extension (from *.c to *.pyd)
python -sBm build --config-setting="--build-option=--native-compile"

The setup.py will then try to build the Cython optimized module creedsolo.pyx if Cython is installed, which can then be imported as import creedsolo instead of import reedsolo, with the same features between both modules. Note: Make sure to use --config-setting singular, because build does not (yet) recognize the plural form contrary to pip.

As an alternative, use conda to install a compiled version for various platforms:

conda install -c conda-forge reedsolo

Various Linux distributions builds are also available, thanks to a network of amazing maintainers:

Package for Gentoo Linux, thanks to maintainer Michał Górny! Package for Debian Linux, thanks to maintainer Faidon Liambotis! Package for Fedora Linux, thanks to maintainer belegdol! Package for Arch Linux, thanks to maintainer Jelle van der Waa! Package for Alpine Linux, thanks to maintainer Michał Polański! Package for ALT Linux, thanks to maintainer Sergey Bolshakov! List of packages for other Linux distributions

Usage

Basic usage with high-level RSCodec class

# Initialization
>>> from reedsolo import RSCodec, ReedSolomonError
>>> rsc = RSCodec(10)  # 10 ecc symbols

# Encoding
# just a list of numbers/symbols:
>>> rsc.encode([1,2,3,4])
b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M'
# bytearrays are accepted and the output will be matched:
>>> rsc.encode(bytearray([1,2,3,4]))
bytearray(b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M')
# encoding a byte string is as easy:
>>> rsc.encode(b'hello world')
b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa'

Note: strings of any length, even if longer than the Galois field, will be encoded as well using transparent chunking.

Note2: it is strongly recommended to always use bytearrays. Using encoded strings is accepted by the RSCodec API, as a convenient facility for neophytes, but encodings such as UTF-8 have variable lengths, so internally the module has to convert to a bytearray. If you just want to protect a string, you do not need to use a bytearray, but if you need to store or send the protected data in a fixed size field, such as in a binary file or a data stream, use a bytearray.

# Decoding (repairing)
>>> rsc.decode(b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa')[0]  # original
b'hello world'
>>> rsc.decode(b'heXlo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0]     # 3 errors
b'hello world'
>>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0]     # 5 errors
b'hello world'
>>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdXX\xf3\xa8\xaa')[0]        # 6 errors - fail
Traceback (most recent call last):
...
reedsolo.ReedSolomonError: Too many (or few) errors found by Chien Search for the errata locator polynomial!

Important upgrade notice for pre-1.0 users: Note that RSCodec.decode() returns 3 variables:

  1. the decoded (corrected) message
  2. the decoded message and error correction code (which is itself also corrected)
  3. and the list of positions of the errata (errors and erasures)

Here is how to use these outputs:

>>> tampered_msg = b'heXlo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa'
>>> decoded_msg, decoded_msgecc, errata_pos = rsc.decode(tampered_msg)
>>> print(decoded_msg)  # decoded/corrected message
bytearray(b'hello world')
>>> print(decoded_msgecc)  # decoded/corrected message and ecc symbols
bytearray(b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa')
>>> print(errata_pos)  # errata_pos is returned as a bytearray, hardly intelligible
bytearray(b'\x10\t\x02')
>>> print(list(errata_pos))  # convert to a list to get the errata positions as integer indices
[16, 9, 2]

Since we failed to decode with 6 errors with a codec set with 10 error correction code (ecc) symbols, let's try to use a bigger codec, with 12 ecc symbols.

>>> rsc = RSCodec(12)  # using 2 more ecc symbols (to correct max 6 errors or 12 erasures)
>>> rsc.encode(b'hello world')
b'hello world?Ay\xb2\xbc\xdc\x01q\xb9\xe3\xe2='
>>> rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=')[0]         # 6 errors - ok, but any more would fail
b'hello world'
>>> rsc.decode(b'helXXXXXXXXXXy\xb2XX\x01q\xb9\xe3\xe2=', erase_pos=[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16])[0]  # 12 erasures - OK
b'hello world'

This shows that we can decode twice as many erasures (where we provide the location of errors ourselves) than errors (with unknown locations). This is the cost of error correction compared to erasure correction.

To get the maximum number of errors or erasures that can be independently corrected (ie, not simultaneously):

>>> maxerrors, maxerasures = rsc.maxerrata(verbose=True)
This codec can correct up to 6 errors and 12 erasures independently
>>> print(maxerrors, maxerasures)
6 12

To get the maximum number of errors and erasures that can be simultaneously corrected, you need to specify the number of errors or erasures you expect:

>>> maxerrors, maxerasures = rsc.maxerrata(erasures=6, verbose=True)  # we know the number of erasures, will calculate how many errors we can afford
This codec can correct up to 3 errors and 6 erasures simultaneously
>>> print(maxerrors, maxerasures)
3 6
>>> maxerrors, maxerasures = rsc.maxerrata(errors=5, verbose=True)  # we know the number of errors, will calculate how many erasures we can afford
This codec can correct up to 5 errors and 2 erasures simultaneously
>>> print(maxerrors, maxerasures)
5 2

Note that if a chunk has more errors and erasures than the Singleton Bound as calculated by the maxerrata() method, the codec will try to raise a ReedSolomonError exception, but may very well not detect any error either (this is a theoretical limitation of error correction codes). In other words, error correction codes are unreliable to detect if a chunk of a message is corrupted beyond the Singleton Bound. If you want more reliability in errata detection, use a checksum or hash such as SHA or MD5 on your message, these are much more reliable and have no bounds on the number of errata (the only potential issue is with collision but the probability is very very low).

Note: to catch a ReedSolomonError exception, do not forget to import it first with: from reedsolo import ReedSolomonError

To check if a message is tampered given its error correction symbols, without decoding, use the check() method:

# Checking
>> rsc.check(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=')  # Tampered message will return False
[False]
>> rmes, rmesecc, errata_pos = rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=')
>> rsc.check(rmesecc)  # Corrected or untampered message will return True
[True]
>> print('Number of detected errors and erasures: %i, their positions: %s' % (len(errata_pos), list(errata_pos)))
Number of detected errors and erasures: 6, their positions: [16, 15, 12, 11, 10, 9]

By default, most Reed-Solomon codecs are limited to characters that can be encoded in 256 bits and with a length of maximum 256 characters. But this codec is universal, you can reduce or increase the length and maximum character value by increasing the Galois Field:

# To use longer chunks or bigger values than 255 (may be very slow)
>> rsc = RSCodec(12, nsize=4095)  # always use a power of 2 minus 1
>> rsc = RSCodec(12, c_exp=12)  # alternative way to set nsize=4095
>> mes = 'a' * (4095-12)
>> mesecc = rsc.encode(mes)
>> mesecc[2] = 1
>> mesecc[-1] = 1
>> rmes, rmesecc, errata_pos = rsc.decode(mesecc)
>> rsc.check(mesecc)
[False]
>> rsc.check(rmesecc)
[True]

Note that the RSCodec class supports transparent chunking, so you don't need to increase the Galois Field to support longer messages, but characters will still be limited to 256 bits (or whatever field you set with c_exp).

If you need to use a variable number of error correction symbols (i.e., akin to variable bitrate in videos encoding), this is possible always possible using RSCodec.decode(nsym=x) and at encoding by setting RSCodec(nsym=y, single_gen=False) and then RSCodec.encode(nsym=x).

If you are trying to decode messages from another Reed-Solomon codec such as MATLAB’s rsenc, make sure you use the same prime number and fcr parameter. In 1-numbered languages (loops and list indices start at 1 instead of 0), the fcr parameter is often hardcoded to 1, which is the case for MATLAB. For compatibility with MATLAB, use the following parameters:

>> rsc = RSCodec(12, prim=285, fcr=1)

For other third-party Reed-Solomon codecs using unknown parameters, an automatic Reed-Solomon parameters searcher can be used to search the viable parameters to decode.

Low-level usage via direct access to math functions

If you want full control, you can skip the API and directly use the library as-is. Here's how:

First you need to init the precomputed tables:

>> import reedsolo as rs
>> rs.init_tables(0x11d)

Pro tip: if you get the error: ValueError: byte must be in range(0, 256), please check that your prime polynomial is correct for your field. Pro tip2: by default, you can only encode messages of max length and max symbol value = 256. If you want to encode bigger messages, please use the following (where c_exp is the exponent of your Galois Field, eg, 12 = max length 2^12 = 4096):

>> prim = rs.find_prime_polys(c_exp=12, fast_primes=True, single=True)[0]
>> rs.init_tables(c_exp=12, prim=prim)

Let's define our RS message and ecc size:

>> n = 255  # length of total message+ecc
>> nsym = 12  # length of ecc
>> mes = "a" * (n-nsym)  # generate a sample message

To optimize, you can precompute the generator polynomial:

>> gen = rs.rs_generator_poly_all(n)

Note: this generates the generator polynomial for all possible nsym, so this can easily be used for variable encoding rate.

Then to encode:

>> mesecc = rs.rs_encode_msg(mes, nsym, gen=gen[nsym])

Let's tamper our message:

>> mesecc[1] = 0

To decode:

>> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym, erase_pos=erase_pos)

Note that both the message and the ecc are corrected (if possible of course). Pro tip: if you know a few erasures positions, you can specify them in a list erase_pos to double the repair power. But you can also just specify an empty list.

You can check how many errors and/or erasures were corrected, which can be useful to design adaptive bitrate algorithms:

>> print('A total of %i errata were corrected over all chunks of this message.' % len(errata_pos))

If the decoding fails, it will normally automatically check and raise a ReedSolomonError exception that you can handle. However if you want to manually check if the repaired message is correct, you can do so:

>> rs.rs_check(rmes + recc, nsym)

This should return a True value if the message is correct (ie, all the values in the syndrome are 0), or False if the message is corrupted (ie, there is at least one non-zero value in the syndrome). The syndrome can be displayed with:

>> rs.rs_calc_syndromes(msg, nsym)

Tip: if you know the message is corrupted but the syndrome is inexplicably all zero (no error detected), make sure you initialized the tables beforehand with rs.init_tables()

If you want to use multiple Reed-Solomon codecs with different parameters simultaneously, you need to backup the globals and restore them before calling reedsolo functions:

>> rs.init_tables()
>> global gf_log, gf_exp, field_charac
>> bak_gf_log, bak_gf_exp, bak_field_charac = gf_log, gf_exp, field_charac

Then at anytime, you can do:

>> global gf_log, gf_exp, field_charac
>> gf_log, gf_exp, field_charac = bak_gf_log, bak_gf_exp, bak_field_charac
>> mesecc = rs.rs_encode_msg(mes, nsym)
>> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym)

The globals backup is not necessary if you use RSCodec, it will be automatically managed.

The speed-optimized C extension creedsolo can be used similarly once compiled or cythonized:

>> import creedsolo as crs
>> codec = crs.RSCodec(10)

If you want to cimport the module, you will need to directly access the full package path:

>> import cython
>> cimport cython
>> cimport creedsolo.creedsolo as crs

Low-level functions allow to construct new APIs on top of this codec, such as automated ECC managing functions for file data protection.

If you want to learn more about which internal functions to use and for what purposes, read the sourcecode's comments (we follow literate programming principles) for more info about how it works and the various parameters you can setup if you need to interface with other RS codecs.

Extended description

The code of wikiversity is here consolidated into a nice API with exceptions handling. The algorithm can correct up to 2*e+v <= nsym, where e is the number of errors, v the number of erasures and nsym = n-k = the number of ECC (error correction code) symbols. This means that you can either correct exactly floor(nsym/2) errors, or nsym erasures (errors where you know the position), and a combination of both errors and erasures. This is called the Singleton Bound, and is the maximum/optimal theoretical number of erasures and errors any error correction algorithm can correct (although there are experimental approaches to go a bit further, named list decoding, not implemented here, but feel free to do pull request!).

The code should work on pretty much any reasonable version of python (3.7+), but I'm only testing on the latest Python version available on Anaconda at the moment (currently 3.10), although there is a unit test on various Python versions to ensure retrocompatibility.

This library is also thoroughly unit tested with branch coverage, so that nearly any encoding/decoding case should be covered. The unit test includes Cython and PyPy too. On top of the unit testing covering mathematical correctedness in this repo here, the code is in practice even more thoroughly covered than shown, via the pyFileFixity <https://github.com/lrq3000/pyFileFixity/>`_ unit test, which is another project using reedsolo for the practical application of on-storage data protection, and which includes a more pragmatic oriented unit test that creates and tamper files to ensure that reedsolo does work in practice to protect and restore data.

The codec is universal, meaning that it should be able to decode any message encoded by any other RS encoder as long as you provide the correct parameters. Beware that often, other RS encoders use internal constant sometimes hardcoded inside the algorithms, such as fcr, which are then hard to find, but if you do, you can supply them to reedsolo.

The work on this module is motivated by the aim to offer a solution for long-term archival of data, although this can and is also used for communication streams. For this purpose, this module is an ideal choice: Reed-Solomon is an optimal (non-quantic) algorithm, it corrects up to the Singleton Bound which is the absolute limit of how much erratas an error-correction algorithm can correct, RS is hence future-proof. The universality of this implementation further ensures that other future implementations of Reed-Solomon should be able to decode data encoded with this universal codec.

Note that if you use higher fields (ie, bigger c_exp), the algorithms will be slower, first because we cannot then use the optimized bytearray() structure but only array.array('i', ...), and also because Reed-Solomon's complexity is quadratic (both in encoding and decoding), so this means that the longer your messages, the quadratically longer it will take to encode/decode!

The algorithm itself can handle messages of a length up to (2^c_exp)-1 symbols per message (or chunk), including the ECC symbols, and each symbol can have a value of up to (2^c_exp)-1 (indeed, both the message length and the maximum value for one character is constrained by the same mathematical reason). By default, we use the field GF(2^8), which means that you are limited to values between 0 and 255 (perfect to represent a single hexadecimal symbol on computers, so you can encode any binary stream) and limited to messages+ecc of maximum length 255. However, you can "chunk" longer messages to fit them into the message length limit. The RSCodec class will automatically apply chunking, by splitting longer messages into chunks and encode/decode them separately; it shouldn't make a difference from an API perspective (ie, from your POV).

Speed optimizations

Thanks to using bytearray and a functional approach (contrary to unireedsolomon, a sibling implementation), the codec has quite reasonable performances despite avoiding hardcoding constants and specific instruction sets optimizations that are not mathematically generalizable (and so we avoid them, as we want to try to remain as close to the mathematical formulations as possible).

In particular, good speed performance at encoding can be obtained by using either PyPy JIT Compiler on the pure-python implementation (reedsolo.py) or either by compiling the Cython extension creedsolo.pyx (which is much more optimized and hence much faster than PyPy).

From our speed tests, encoding rates of several MB/s can be expected with PyPy JIT, and 14.3 MB/s using the Cython extension creedsolo on an Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz (benchmarked with pyFileFixity's ecc_speedtest.py).

Decoding remains much slower, and less optimized, but more complicated to do so. However, the rationale to focus optimization efforts primarily on encoding and not decoding is that users are more likely to spend most of their processing time encoding data, and much less decoding, as encoding needs to be done indiscriminately apriori to protect data, whereas decoding happens only aposteriori on data that the user knows is tampered, so this is a much reduced subset of all the protected data (hopefully).

To use the Cython implementation, it is necessary to pip install cython==3.0.0b2 and to install a C++ compiler (Microsoft Visual C++ 14.x for Windows and Python 3.10+), read the up-to-date instructions in the official wiki. Then simply cd to the root of the folder where creedsolo.pyx is, and type python setup.py build_ext --inplace --cythonize. Alternatively, it is possible to generate just the C++ code by typing cython -3 creedsolo.pyx. When building a distributable egg or installing the module from source, the Cython module can be transpiled and compiled if both Cython and a C compiler are installed and the --cythonize flag is supplied to the setup.py, otherwise by default only the pure-python implementation and the .pyx cython source code will be included, but the binary won't be in the wheel.

Then, use from creedsolo import RSCodec instead of importing from the reedsolo module, and finally only feed bytearray() objects to the RSCodec object. Exclusively using bytearrays is one of the reasons creedsolo is faster than reedsolo. You can convert any string by specifying the encoding: bytearray("Hello World", "UTF-8").

Note that there is an inherent limitation of the C implementation which cannot work with higher galois fields than 8 (= characters of max 255 value) because the C implementation only works with bytearrays, and bytearrays only support characters up to 255. If you want to use higher galois fields, you need to use the pure python version, which includes a fake _bytearray function that overloads the standard bytearray with an array.array("i", ...) in case galois fields higher than 8 are used to init_tables(), or rewrite the C implementation to use lists instead of bytearrays (which will be MUCH slower so this defeats the purpose and you are better off simply using the pure python version under PyPy - an older version of the C implementation was doing just that, and without bytearrays, all performance gains were lost, hence why the bytearrays were kept despite the limitations).

Edge cases

Although sanity checks are implemented whenever possible and when they are not too much resource consuming, there are a few cases where messages will not be decoded correctly without raising an exception:

  • If an incorrect erasure location is provided, the decoding algorithm will just trust the provided locations and create a syndrome that will be wrong, resulting in an incorrect decoded message. In case reliability is critical, always use the check() method after decoding to check the decoding did not go wrong.
  • Reed-Solomon algorithm is limited by the Singleton Bound, which limits not only its capacity to correct errors and erasures relatively to the number of error correction symbols, but also its ability to check if the message can be decoded or not. Indeed, if the number of errors and erasures are greater than the Singleton Bound, the decoder has no way to mathematically know for sure whether there is an error at all, it may very well be a valid message (although not the message you expect, but mathematically valid nevertheless). Hence, when the message is tampered beyond the Singleton Bound, the decoder may raise an exception, but it may also return a mathematically valid but still tampered message. Using the check() method cannot fix that either. To work around this issue, a solution is to use parity or hashing functions in parallel to the Reed-Solomon codec: use the Reed-Solomon codec to repair messages, use the parity or hashing function to check if there is any error. Due to how parity and hashing functions work, they are much less likely to produce a false negative than the Reed-Solomon algorithm. This is a general rule: error correction codes are efficient at correcting messages but not at detecting errors, hashing and parity functions are the adequate tool for this purpose.

Migration from v1.x to v2.x

If you used reedsolo v1.x, then to upgrade to v2.x, a few changes in the build requirements, the build system and API must be considered.

One major change is that Cython>=v3.0.0b2 is required to cythonize creedsolo.pyx. To ease migration for operating systems where python packages pre-releases are not available, the intermediary creedsolo.c is also shipped in the standard distribution (the tar.gz file) to allow compilation with any C compiler, without requiring Cython.

Furthermore, the packaging system was overhauled to be PEP 517 standard compliant, so that it now supports build isolation by default, and it uses a src-layout.

While we tried to keep the import API the same (you can still do import reedsolo as rs; codec = rs.RSCodec(10) and similarly import creedsolo as crs. However, if you used to cimport creedsolo as crs using the fast c-import system provided by Cython, now you will need to cimport creedsolo.creedsolo as crs.

Indeed, for Linux distributions package maintainers, it's important to note the module is now using a "src-layout", instead of the "single-module-layout" before, so this may require some adjustments in packages building processes.

Furthermore, wheels with a precompiled creedsolo.pyd extension are now built for multiple platforms and Python releases and uploaded to PyPi, thanks to cibuildwheel, and the process is automated with a GitHub Action. In future releases, we will try to improve on build reproducibility, such as by implementing a lockfile (but not there yet, there is no standard for that) and moving away from setuptools (potentially to meson).

Support for Python 2.7 and Python <= 3.6 was dropped as advised elsewhere, as only the pure python implementation remained retrocompatible, but not the cython extension, so that it is better for older Py2.7 users to simply stick to the fully functional reedsolo v1.7.0. For Python 3.6, support was dropped because these environments are not supported officially anymore by GitHub Actions, so it is harder to unit test and hence no guarantee of correctedness can be provided anymore in an automated fashion, so it's better to also use reedsolo v1.7.0 for these older Py3 versions.

About API changes, a few bugfixes were implemented in the pure python implementation, but breaking changes were limited as much as possible (if there is any, it is unintended). For the creedsolo extension, there are LOTS of changes, hence why the major version change (we try to follow SemVer). We will not list everything here, but the biggest breaking change is that now internally, everything is either a bytearray, or a CPython array('i', ...). So this means that when interacting with creedsolo, you want to always supply a bytearray object, you can't just provide a list or a string anymore. For reedsolo, this is still supported, since it transparently converts to a bytearray internally, for ease of use.

For the pure python implementation reedsolo, this should not change much, it should be retrocompatible with lists (there are a few checks in place to autodetect and convert lists into bytearrays whenever necessary - but only in RSCodec, not in lower level functions if that's what you used!).

However, for the cythonized extension creedsolo, these changes are breaking compatibility with v1.x: if you used bytearray everywhere whenever supplying a list of values into creedsolo (both for the data and erasures_pos), then all is well, you are good to go! On the other hand, if you used list objects or other types in some places, you are in for some errors.

The good news is that, thanks to these changes, both implementations are much faster, but especially creedsolo, which now encodes at a rate of 15-20 MB/s (yes that's BYTES, not bits!). This however requires Cython >= 3.0.0b2, and is incompatible with Python 2 (the pure python reedsolo is still compatible, but not the cythonized extension creedsolo).

In practice, there is likely very little you need to change, just add a few bytearray() calls here and there. For a practical example of what was required to migrate, see the commits for pyFileFixity migration.

Projects using reedsolo

Reedsolo is a critical component of numerous solutions, such as:

  • Matter (ex-Project CHIP) - The new standard for the Internet of Things (IoT): Matter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers and increasing compatibility for consumers, guided by the Connectivity Standards Alliance.
  • esp-idf - Espressif IoT Development Framework. Official development framework for Espressif SoCs, such as ESP32, which are very widespread reprogrammable electronic cheaps for scientific, prototype and DIY projects, especially with Arduino and MicroPython.
  • esptool - A Python-based, open-source, platform-independent utility to communicate with the ROM bootloader in Espressif chips.
  • pyFileFixity - A suite of tools for long term archival of files.
  • amodem - Audio MODEM Communication Library in Python, allowing true air-gapped communication (via a speaker and a microphone), or an audio cable (for higher transmission speed).
  • SteganoGAN - SteganoGAN is a tool for creating steganographic images using adversarial training.
  • galacteek - Multi-platform browser for the distributed web.
  • ofrak - OFRAK (Open Firmware Reverse Analysis Konsole) is a binary analysis and modification platform.
  • HoloCubic AIO - All-in-One open-source firmware for the HoloCubic device with a wide features set.
  • MicroPython-Stubber - Boost MicroPython productivity in VSCode: Generate and use stubs for different micropython firmwares to use with vscode and pylance or pylint.
  • qr-backup - Paper backup of files using QR codes.
  • Jade - Jade Hardware Wallet.
  • pied-piper - Defunct popular module for data transfer over sound waves.
  • qreader - A defunct pure python QR code reader.
  • sonicky - Proof-of-concept Python and Android modules for connectionless ultrasonic message transfer.
  • neighborhood-connectivity - An example app that implements a noisy communication between clique of thread group with very high error correction handling ability and O(1) rounds of messages sending.
  • audiotagger - Clever use of error correction codes to wirelessly synchronize multiple concurrent video feeds of amateur video filmmakers by injecting AFSK packets with timestamp and location metadata in the audio channel communicated via radios.
  • Several research papers used reedsolo, see a list here.

And many, many more!

  • "Reed-Solomon codes for coders", free practical beginner's tutorial with Python code examples on WikiVersity. Partially written by one of the authors of the present software.
  • "Algebraic codes for data transmission", Blahut, Richard E., 2003, Cambridge university press. Readable online on Google Books. This book was pivotal in helping to understand the intricacies of the universal Berlekamp-Massey algorithm (see figures 7.5 and 7.10).
  • If you want a more mathematically transparent but less optimized implementation, read the sibling open-source project unireedsolomon, also co-authored by the maintainer of reedsolo, so that the codebase is very similar (although reedsolo is more mature and has more bugfixes - unireedsolomon should only be used for learning purposes!).

Similar projects

Here is a non-exhaustive list of similar projects (ie, projects implementing a Reed-Solomon codec):

  • galois, a Numba JIT-optimized extension module for Numpy, which implements a Reed-Solomon codec and NTT transforms.
  • generalizedReedSolomon, a variant of an implementation of Reed-Solomon that is more parallelizable and interestingly can encode messages of arbitrary length without chunking (the chunking is kind of integrated in the parallelization). Internally uses numpy’s fft and ifft. Maybe translatable to use Number Theoretic Transform (NTT) instead of FFT.

Authors

This module was conceived and developed by Tomer Filiba in 2012.

It was further extended and is currently maintained by Stephen Karl Larroque since 2015.

And several other contributors helped improve and make it more robust, thanks a lot to them!

Contributors

For a list of all contributors, please see the GitHub Contributors graph and the commits history.

License

This software is released under your choice of the Unlicense or the MIT-0 (MIT No Attribution) License. Both licenses are public-domain-equivalent licenses, as intended by the original author Tomer Filiba.

reedsolomon's People

Contributors

elvis-epx avatar felixonmars avatar jbosboom avatar lrq3000 avatar m-rossi avatar mgorny avatar musicinmybrain avatar projectgus avatar rotorgit avatar tomerfiliba avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reedsolomon's Issues

Cython check error: "object has no attribute 'chunk'"

I tried to use cython and installed with
sudo python3 setup.py install --native-compile

Installation seem to have worked but I get this error calling the check method:

File "creedsolo.pyx", line 870, in creedsolo.RSCodec.check
AttributeError: 'RSCodec' object has no attribute 'chunk'

Am I doing something wrong?

Tag release v1.5.4 on github

Hi! I'm packaging this module for NixOS. It seems that the pypi tarball does not include the tests dir, so I'm using the source from the github repo. It would be useful if the latest release, v1.5.4 were tagged here.

git tag v1.5.4 73926cdf81b39009bd6e46c8d49f3bbc0eaad4e4
git push origin v1.5.4

How to use for Bit Correction

Data length = 64 bit (data) + 32 bit (RS padding) = 96 bit.

Current code is trying to correct in Byte Unit.
But I want to correct in bit unit so that it can correct as many bit as possible.

Thank you!

Creedsolo compilation failed with creedsolo.c:32027:5: error: lvalue required as increment operand

Python 3.10.7
GCC 12.2.0
archlinux (manjaro)

Looks like a simple bug with the increment operator but could this somehow be due to a breaking change introduced by GCC 12 because I successfully installed it the other day on another machine with GCC 11.3 (I don't recall anything about this compilation showing but Cython is installed there too)

Is there any workaround with a binary or a way to ask pip to ignore the 'cython optimised module' ? I saw suggestions in #32 but they did not help.

command:
pip install reedsolo

output:

  Using cached reedsolo-1.5.4.tar.gz (271 kB)
  Preparing metadata (setup.py) ... done
Building wheels for collected packages: reedsolo
  Building wheel for reedsolo (setup.py) ... error
  error: subprocess-exited-with-error
  
  × python setup.py bdist_wheel did not run successfully.
  │ exit code: 1
  ╰─> [124 lines of output]
      Cython is installed, building creedsolo module
      running bdist_wheel
      running build
      running build_py
      creating build
      creating build/lib.linux-x86_64-cpython-310
      copying reedsolo.py -> build/lib.linux-x86_64-cpython-310
      running build_ext
      building 'creedsolo' extension
      creating build/temp.linux-x86_64-cpython-310
      gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug -flto=auto -ffat-lto-objects -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug -flto=auto -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug -flto=auto -fPIC -I/usr/include/python3.10 -c creedsolo.c -o build/temp.linux-x86_64-cpython-310/creedsolo.o
      creedsolo.c: In function ‘__pyx_tp_dealloc_array’:
      creedsolo.c:32027:5: error: lvalue required as increment operand
      32027 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32029:5: error: lvalue required as decrement operand
      32029 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__pyx_tp_dealloc_memoryview’:
      creedsolo.c:32338:5: error: lvalue required as increment operand
      32338 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32340:5: error: lvalue required as decrement operand
      32340 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__pyx_tp_dealloc__memoryviewslice’:
      creedsolo.c:32588:5: error: lvalue required as increment operand
      32588 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32590:5: error: lvalue required as decrement operand
      32590 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__Pyx_InitGlobals’:
      creedsolo.c:33787:1: warning: ‘PyEval_InitThreads’ is deprecated [-Wdeprecated-declarations]
      33787 | PyEval_InitThreads();
            | ^~~~~~~~~~~~~~~~~~
      In file included from /usr/include/python3.10/Python.h:130,
                       from creedsolo.c:16:
      /usr/include/python3.10/ceval.h:122:37: note: declared here
        122 | Py_DEPRECATED(3.9) PyAPI_FUNC(void) PyEval_InitThreads(void);
            |                                     ^~~~~~~~~~~~~~~~~~
      creedsolo.c: In function ‘__Pyx_ParseOptionalKeywords’:
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      In file included from /usr/include/python3.10/unicodeobject.h:1046,
                       from /usr/include/python3.10/Python.h:83:
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c: In function ‘__Pyx_decode_c_string’:
      creedsolo.c:37955:9: warning: ‘PyUnicode_FromUnicode’ is deprecated [-Wdeprecated-declarations]
      37955 |         return PyUnicode_FromUnicode(NULL, 0);
            |         ^~~~~~
      /usr/include/python3.10/cpython/unicodeobject.h:551:42: note: declared here
        551 | Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_FromUnicode(
            |                                          ^~~~~~~~~~~~~~~~~~~~~
      error: command '/usr/bin/gcc' failed with exit code 1
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for reedsolo
  Running setup.py clean for reedsolo
Failed to build reedsolo
Installing collected packages: reedsolo
  Running setup.py install for reedsolo ... error
  error: subprocess-exited-with-error
  
  × Running setup.py install for reedsolo did not run successfully.
  │ exit code: 1
  ╰─> [126 lines of output]
      Cython is installed, building creedsolo module
      running install
      /usr/lib/python3.10/site-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
        warnings.warn(
      running build
      running build_py
      creating build
      creating build/lib.linux-x86_64-cpython-310
      copying reedsolo.py -> build/lib.linux-x86_64-cpython-310
      running build_ext
      building 'creedsolo' extension
      creating build/temp.linux-x86_64-cpython-310
      gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug -flto=auto -ffat-lto-objects -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug -flto=auto -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug -flto=auto -fPIC -I/usr/include/python3.10 -c creedsolo.c -o build/temp.linux-x86_64-cpython-310/creedsolo.o
      creedsolo.c: In function ‘__pyx_tp_dealloc_array’:
      creedsolo.c:32027:5: error: lvalue required as increment operand
      32027 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32029:5: error: lvalue required as decrement operand
      32029 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__pyx_tp_dealloc_memoryview’:
      creedsolo.c:32338:5: error: lvalue required as increment operand
      32338 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32340:5: error: lvalue required as decrement operand
      32340 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__pyx_tp_dealloc__memoryviewslice’:
      creedsolo.c:32588:5: error: lvalue required as increment operand
      32588 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32590:5: error: lvalue required as decrement operand
      32590 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__Pyx_InitGlobals’:
      creedsolo.c:33787:1: warning: ‘PyEval_InitThreads’ is deprecated [-Wdeprecated-declarations]
      33787 | PyEval_InitThreads();
            | ^~~~~~~~~~~~~~~~~~
      In file included from /usr/include/python3.10/Python.h:130,
                       from creedsolo.c:16:
      /usr/include/python3.10/ceval.h:122:37: note: declared here
        122 | Py_DEPRECATED(3.9) PyAPI_FUNC(void) PyEval_InitThreads(void);
            |                                     ^~~~~~~~~~~~~~~~~~
      creedsolo.c: In function ‘__Pyx_ParseOptionalKeywords’:
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      In file included from /usr/include/python3.10/unicodeobject.h:1046,
                       from /usr/include/python3.10/Python.h:83:
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c: In function ‘__Pyx_decode_c_string’:
      creedsolo.c:37955:9: warning: ‘PyUnicode_FromUnicode’ is deprecated [-Wdeprecated-declarations]
      37955 |         return PyUnicode_FromUnicode(NULL, 0);
            |         ^~~~~~
      /usr/include/python3.10/cpython/unicodeobject.h:551:42: note: declared here
        551 | Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_FromUnicode(
            |                                          ^~~~~~~~~~~~~~~~~~~~~
      error: command '/usr/bin/gcc' failed with exit code 1
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
error: legacy-install-failure

× Encountered error while trying to install package.
╰─> reedsolo

New pypi version?

I'd like to use some of the newer features, namely the error_pos argument to RSCodec.decode. It seems like the latest version in pypi is 0.3. Is it possible to get the latest version pushed?

The project description is inaccurate

I'm sorry for being picky but the description right now says:

Pure-Python Reed Solomon encoder/decoder

However, the default install includes a C module, so that's not really "Pure-Python" and could lead to confusion. Would it be fine to change that to just "Python Reed Solomon encoder/decoder"?

Symbols other than 8 bit

Hi,
I would like to use an alphabet of 2^5 possible symbols (base32 encoding) for my message and code. I tried setting c_exp to 5 and get the following error:

>>> rs = reedsolo.RSCodec(nsym=2, nsize=8, c_exp=5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.4/dist-packages/reedsolo-0.5-py3.4-linux-x86_64.egg/reedsolo.py", line 756, in __init__
    init_tables(prim, generator, c_exp)
  File "/usr/local/lib/python3.4/dist-packages/reedsolo-0.5-py3.4-linux-x86_64.egg/reedsolo.py", line 201, in init_tables
    gf_exp[i] = x # compute anti-log for this value and store it in a table
ValueError: byte must be in range(0, 256)

I want to get something like a flight code (length 8 digits/characters) with error correction.
Has anyone tried using different symbols? Is it possible using the base library instead of the hight level API?
Thanks

MIT license option needs a copyright notice and license text

In the License section of README.rst, you state:

If the Public Domain is not adequate for your purpose, you can instead consider this module under the MIT License as you prefer.

However, one of the provisions of the MIT license is that the copyright notice and license text shall be included in all copies. It is impossible to comply with this unless you add an original MIT license text with appropriate copyright notice—preferably in its own file, like LICENSE-MIT, or perhaps in an added section in the existing LICENSE file.

Please see this documentation from the Fedora Linux project for further explanation. (From the perspective of the Fedora Linux project, the Public Domain option is perfectly adequate.)

Feature Req: Support Ignoring Symbols for Error/Erasure Correction

The following is mentioned in the README:

Although sanity checks are implemented whenever possible and when they are not too much resource consuming, there are a few cases where messages will not be decoded correctly without raising an exception:

  • If an incorrect erasure location is provided, the decoding algorithm will just trust the provided locations and create a syndrome that will be wrong, resulting in an incorrect decoded message. In case reliability is critical, always use the check() method after decoding to check the decoding did not go wrong.

A fairly straightforward method to enable variable rate coding would be to not transmit/store trailing error or data symbols, so you could increase or decrease the rate at will, respectively. Half of this functionality is already fully available, since if you skip error symbols, you can simply mark them as having been erased.

However, if you choose to skip data symbols (input them as zero and then don't store/transmit), there is no way to inform the decoder that these zeroed symbols (which will have been substituted back in as zeros before calling the decoder) are definitely correct.

For this and perhaps other uses that I am not aware of yet, a feature by which you could do the opposite of the existing functionality — inform the decoder that certain symbols are definitely correct — could be extremely useful.

number of errors seen

Hi,

Maybe you can extend the api so that decode() returns a tuple:
(data, n_errors_seen)
?
That way one can anticipate by lowering baudrates and such.

The current state of this module

Thank you for your interest in or for using this module. Before @tomerfiliba made it 10 years ago and I took the mantle 7 years ago, error correction was something that was available only in proprietary softwares, or hidden in the entrails of academic works as side functions often mixed in with the core routines of these academic projects that were aimed at other purposes than pure error correction. This module was an experiment to provide an out-of-the-box readily useable yet multifunctional tool for general error correction, that could then be used for numerous practical applications, from QR code generators and readers, to hard drive data protection and recovery and of course data stream denoising.

While I am glad of the positive impact this project had, it was never intended to be used so intensively in production. It was made as an educational tool and as robust as possible proof of concept of a universal Reed-Solomon encoder/decoder, to show the ropes with most of the maths worked out and implemented in a working Python package to hopefully inspire others from making a better suite of tools, using this as a reference to check the maths.

But now this Python module is used as-is in numerous other projects (thousands from what GitHub shown me, and likely more given there are >300K monthly downloads!), that are relying on it for the very sensitive task of ensuring data preservation and reliability. This is a huge responsibility, and unfortunately there is no one maintaining this project but me, someone who is not even in the field of signal processing (I worked on some such projects, but this is not my specialty, this project was just a "fun" side project).

In the last 2 years, I hoped someone would jump in to replace this project with another more robust one, but it seems no one did. I will hence continue to try to maintain this project once in a while, very infrequently and with no guarantee, just to ensure the project still continues to work. But no new features will be implemented. PRs are very welcome to bug fixes, otherwise they may not ever be corrected.

I aim to maintain the software to run as-is, and it's already a lot of work given the fast pace of the Python ecosystem evolution. If anyone else wants to step in to maintain or even develop further the project (under open-source of course), please reply in this thread and I will consider your application.

Enable multiple reedsolo at the same time

I have code that needs to verify two different reedsolo checksums at different times.

one with c_exp=8, the second one with c_exp=6.

My current solution is to copy reedsolo.py to reedsolo6.py and import both reedsolo and reedsolo6 so i can have two instances.

It would be nice if this were supported out of the box :-)

docs: Cython version can not be installed

I follow the steps described in the read me to install the cython version, with no luck. Specifically:

jcea@jcea:/tmp/ram$ pip install --upgrade reedsolo --install-option="--cythonize" --verbose

Usage:   
  pip install [options] <requirement specifier> [package-index-options] ...
  pip install [options] -r <requirements file> [package-index-options] ...
  pip install [options] [-e] <vcs project url> ...
  pip install [options] [-e] <local project path> ...
  pip install [options] <archive url/path> ...

no such option: --install-option

Python 3.11.3
pip 23.1.2
cython 0.29.34

Array of bit/boolean values

Hi,
thank you for the library.
I am not sure if it is possible but I am currently trying to encode a single value of 20 bits, so I am trying to feed the encoder with a boolean array of length 20. e.g. [true, true, false, true, false ....]

After encoding, 60 bits should be produced (in total)
I would like to be able to correct 5 errors.
Size of the alphabet q = 16
blocklength n = 15
Message length k = 5

I am not sure what parameters I have to use - of if I am using them correctly.
I have a legacy Java implementation, but the produced data differs....
Can somebody help me to understand the correct use of the library for the parameters above...?

Missing tag for 1.7.0

Could you please push the tag for 1.7.0 release? We use tags to make Gentoo packages here.

Wrong decoding with edge-condition parameters

I think it's a bug, but I'm not 100% sure, because I'm playing with RS codec for just few hours.

>>> import reedsolo
>>> c = reedsolo.RSCodec(nsym=0, nsize=1)
>>> c.encode(b'aaa')
bytearray(b'aaa')
>>> c.decode(b'aaa')
bytearray(b'')

rs_calc_syndromes could not find injected error

I'm not sure if it's just me.

But I first found this issue with the wikiversity article, so I thought the python reed-solo wouldn't have this issue, but I'm encountering the same issue.


msg_in = [ 0x40, 0xd2, 0x75, 0x47, 0x76, 0x17, 0x32, 0x06, 0x27, 0x26, 0x96, 0xc6, 0xc6, 0x96, 0x70, 0xec ]
msg = rs_encode_msg(msg_in, 10)

synd = rs_calc_syndromes(msg, 10)
print("Message initial: ")
for i in range(0,len(msg)):
    print(hex(msg[i]), end=' ')
print(synd) # not corrupted message = all 0 syndromes
msg[0] = 0  # deliberately damage the message
synd = rs_calc_syndromes(msg, 10)
print("Message after: ")
for i in range(0,len(msg)):
    print(hex(msg[i]), end=' ')
print(synd) # when corrupted, the syndromes will be non zero

synd = rs_calc_syndromes(msg, 10)
msg = rs_correct_errata(msg, synd, [0]) # [0] is the list of the erasures locations, here it's the first character, at position 0
print(hex(msg[0]))

image

After injecting error by changing the first bit of the message into zero, I recalculate the syndromes, but the resulting syndrome is exactly the same! Meaning rs_correct_errata makes no correction.

Could someone let me know whether I'm just not understanding.

python 3.10 installation issue

Python: 3.10
Reedsolo: 1.5.4
OS: Linux / Slackware

Collecting reedsolo
  Using cached reedsolo-1.5.4.tar.gz (271 kB)
Building wheels for collected packages: reedsolo
  Building wheel for reedsolo (setup.py) ... error
  ERROR: Command errored out with exit status 1:
   command: /usr/bin/python3 -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-yfvlvd6u/reedsolo_8cc3b5cd27ba467ea55234248fb4edf0/setup.py'"'"'; __file__='"'"'/tmp/pip-install-yfvlvd6u/reedsolo_8cc3b5cd27ba467ea55234248fb4edf0/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /tmp/pip-wheel-0hk94tmt
       cwd: /tmp/pip-install-yfvlvd6u/reedsolo_8cc3b5cd27ba467ea55234248fb4edf0/
  Complete output (161 lines):
  Cython is installed, building creedsolo module
  running bdist_wheel
  running build
  running build_py
  creating build
  creating build/lib.linux-i686-3.10
  copying reedsolo.py -> build/lib.linux-i686-3.10
  running build_ext
  building 'creedsolo' extension
  creating build/temp.linux-i686-3.10
  gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/usr/include/python3.10 -c creedsolo.c -o build/temp.linux-i686-3.10/creedsolo.o
  creedsolo.c: In function ‘__pyx_tp_dealloc_array’:
  creedsolo.c:32027:5: error: lvalue required as increment operand
  32027 |     ++Py_REFCNT(o);
        |     ^~
  creedsolo.c:32029:5: error: lvalue required as decrement operand
  32029 |     --Py_REFCNT(o);
        |     ^~
  creedsolo.c: In function ‘__pyx_tp_dealloc_memoryview’:
  creedsolo.c:32338:5: error: lvalue required as increment operand
  32338 |     ++Py_REFCNT(o);
        |     ^~
  creedsolo.c:32340:5: error: lvalue required as decrement operand
  32340 |     --Py_REFCNT(o);
        |     ^~
  creedsolo.c: In function ‘__pyx_tp_dealloc__memoryviewslice’:
  creedsolo.c:32588:5: error: lvalue required as increment operand
  32588 |     ++Py_REFCNT(o);
        |     ^~
  creedsolo.c:32590:5: error: lvalue required as decrement operand
  32590 |     --Py_REFCNT(o);
        |     ^~
  creedsolo.c: In function ‘__Pyx_InitGlobals’:
  creedsolo.c:33787:1: warning: ‘PyEval_InitThreads’ is deprecated [-Wdeprecated-declarations]
  33787 | PyEval_InitThreads();
        | ^~~~~~~~~~~~~~~~~~
  In file included from /usr/include/python3.10/Python.h:144,
                   from creedsolo.c:16:
  /usr/include/python3.10/ceval.h:122:37: note: declared here
    122 | Py_DEPRECATED(3.9) PyAPI_FUNC(void) PyEval_InitThreads(void);
        |                                     ^~~~~~~~~~~~~~~~~~
  creedsolo.c: In function ‘__Pyx_ParseOptionalKeywords’:
  creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
  35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                     ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
    446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
        |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
  creedsolo.c:35153:21: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
  35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                     ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
    580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
        |                                             ^~~~~~~~~~~~~~~~~~~
  creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
  35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                     ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
    446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
        |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
  creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
  35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                     ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
    446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
        |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
  creedsolo.c:35153:21: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
  35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                     ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
    580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
        |                                             ^~~~~~~~~~~~~~~~~~~
  creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
  35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                     ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
    446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
        |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
  creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
  35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                         ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
    446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
        |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
  creedsolo.c:35169:25: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
  35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                         ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
    580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
        |                                             ^~~~~~~~~~~~~~~~~~~
  creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
  35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                         ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
    446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
        |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
  creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
  35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                         ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
    446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
        |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
  creedsolo.c:35169:25: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
  35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                         ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
    580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
        |                                             ^~~~~~~~~~~~~~~~~~~
  creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
  35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
        |                         ^
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
    446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
        |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
  creedsolo.c: In function ‘__Pyx_decode_c_string’:
  creedsolo.c:37955:9: warning: ‘PyUnicode_FromUnicode’ is deprecated [-Wdeprecated-declarations]
  37955 |         return PyUnicode_FromUnicode(NULL, 0);
        |         ^~~~~~
  In file included from /usr/include/python3.10/unicodeobject.h:1046,
                   from /usr/include/python3.10/Python.h:96,
                   from creedsolo.c:16:
  /usr/include/python3.10/cpython/unicodeobject.h:551:42: note: declared here
    551 | Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_FromUnicode(
        |                                          ^~~~~~~~~~~~~~~~~~~~~
  error: command '/usr/bin/gcc' failed with exit code 1
  ----------------------------------------
  ERROR: Failed building wheel for reedsolo

Thanks!

Code rarely raises ZeroDivisionError

In the file reedsolo.py at line 499: magnitude = gf_div(y, err_loc_prime)

this will divide by zero if err_loc_prime is zero.

You should add a check for err_loc_prime == 0

like this:

        y = gf_poly_eval(err_eval[::-1], Xi_inv) # numerator of the Forney algorithm (errata evaluator evaluated)
        y = gf_mul(gf_pow(Xi, 1), y)

        if err_loc_prime == 0:
            raise ReedSolomonError("Could not find error magnitude") 

        # Compute the magnitude
        magnitude = gf_div(y, err_loc_prime) # magnitude value of the error, calculated by the Forney algorithm (an equation in fact): dividing the errata evaluator with the errata locator derivative gives us the errata magnitude (ie, value to repair) the ith symbol
        E[err_pos[i]] = magnitude # store the magnitude for this error into the magnitude polynomial

Problems with ReedSolomonError Exception handling

here's the relevant snippets of my code, which works fine with errors not exceeding RS capability

N, K = 255, 223
rsc = RSCodec(N-K, nsize=N)

for i in range(0,Npixels*N//K,N):
    try:
        rx_byte = np.append(rx_byte,np.uint8(rsc.decode(rx_enc[i:i+N])[0]))
    except ReedSolomonError:
        rx_byte = np.append(rx_byte,rx_enc[i:i+K])

I get

Traceback (most recent call last):

  File "  ", line 36, in <module>
    rx_byte = np.append(rx_byte,np.uint8(rsc.decode((rx_enc[i:i+N]))[0]))

  File "C:\ProgramData\Anaconda3\lib\site-packages\reedsolo.py", line 924, in decode
    rmes, recc, errata_pos = rs_correct_msg(chunk, nsym, fcr=self.fcr, generator=self.generator, erase_pos=e_pos, only_erasures=only_erasures)

  File "C:\ProgramData\Anaconda3\lib\site-packages\reedsolo.py", line 747, in rs_correct_msg
    err_pos = rs_find_errors(err_loc[::-1], len(msg_out), generator)

  File "C:\ProgramData\Anaconda3\lib\site-packages\reedsolo.py", line 693, in rs_find_errors
    raise ReedSolomonError("Too many (or few) errors found by Chien Search for the errata locator polynomial!")

ReedSolomonError: Too many (or few) errors found by Chien Search for the errata locator polynomial!

During handling of the above exception, another exception occurred:

Traceback (most recent call last):

  File "  ", line 37, in <module>
    except ReedSolomonError:

NameError: name 'ReedSolomonError' is not defined

1.7.0: pep517 is not building DSO `creedsolo` module

Looks like something is wrog when pep517 is used and DSO modules is not build

+ /usr/bin/python3 -sBm build -w --no-isolation
* Getting build dependencies for wheel...
running egg_info
creating reedsolo.egg-info
writing reedsolo.egg-info/PKG-INFO
writing dependency_links to reedsolo.egg-info/dependency_links.txt
writing top-level names to reedsolo.egg-info/top_level.txt
writing manifest file 'reedsolo.egg-info/SOURCES.txt'
reading manifest file 'reedsolo.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
adding license file 'LICENSE'
writing manifest file 'reedsolo.egg-info/SOURCES.txt'
* Building wheel...
running bdist_wheel
running build
running build_py
creating build
creating build/lib
copying reedsolo.py -> build/lib
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/wheel
copying build/lib/reedsolo.py -> build/bdist.linux-x86_64/wheel
running install_egg_info
running egg_info
writing reedsolo.egg-info/PKG-INFO
writing dependency_links to reedsolo.egg-info/dependency_links.txt
writing top-level names to reedsolo.egg-info/top_level.txt
reading manifest file 'reedsolo.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
adding license file 'LICENSE'
writing manifest file 'reedsolo.egg-info/SOURCES.txt'
Copying reedsolo.egg-info to build/bdist.linux-x86_64/wheel/reedsolo-1.7.0-py3.8.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/reedsolo-1.7.0.dist-info/WHEEL
creating '/home/tkloczko/rpmbuild/BUILD/reedsolomon-1.7.0/dist/.tmp-h1l51r67/reedsolo-1.7.0-py3-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it
adding 'reedsolo.py'
adding 'reedsolo-1.7.0.dist-info/LICENSE'
adding 'reedsolo-1.7.0.dist-info/METADATA'
adding 'reedsolo-1.7.0.dist-info/WHEEL'
adding 'reedsolo-1.7.0.dist-info/top_level.txt'
adding 'reedsolo-1.7.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built reedsolo-1.7.0-py3-none-any.whl

Bug in bytearray from decoder

Hi,

I found a bug in the decoding part. It is good in Windows. In Linux and Mac OS, we found the decoder return a list [bytearray 1, bytearray 2, bytearray 3] not just a bytearray. The bytearray 1 is what we need in the decoding part. However, I am not sure why it is different in different operating systems.

In my package, we do the follow sentence to fix this problem (TypeError: 'bytearray' object cannot be interpreted as an integer):

self.tool = RSCodec(3)

decode_byte_list = list(self.tool.decode(byte_list))

if type(decode_byte_list[0]) is not int:
    decode_byte_list = list(decode_byte_list[0])

Best and FYI,

Zhang

Code does not work when decoding chunked messages when erase_pos is used

Example failure

import reedsolo
rsc = reedsolo.RSCodec(30)
encoded = rsc.encode(b'0' * 226)
_, _, _ = rsc.decode(encoded, erase_pos=[255], only_erasures=True)

output:

Traceback (most recent call last):
  File "demo.py", line 4, in <module>
    _, _, _ = rsc.decode(encoded, erase_pos=[255], only_erasures=True)
  File "reedsolo.py", line 927, in decode
    rmes, recc, errata_pos = rs_correct_msg(chunk, nsym, fcr=self.fcr, generator=self.generator, erase_pos=e_pos, only_erasures=only_erasures)
  File "reedsolo.py", line 731, in rs_correct_msg
    msg_out[e_pos] = 0
IndexError: bytearray index out of range

Erasure position calculation for the 2nd chunk onward are incorrect due to this line:

922                 e_pos = [x for x in erase_pos if x <= self.nsize]
923                 ...
924                 erase_pos = [x - (self.nsize+1) for x in erase_pos if x > self.nsize]

it should be:

922                 e_pos = [x for x in erase_pos if x < self.nsize]
923                 ...
924                 erase_pos = [x - (self.nsize) for x in erase_pos if x >= self.nsize]

ERROR: Failed building wheel for reedsolo

  • Microsoft Windows 10 Home x64
  • Python 3.8.6

Unexpected behaviour

pip install reedsolo

fails with:

Collecting reedsolo
  Using cached reedsolo-1.5.4.tar.gz (271 kB)
Building wheels for collected packages: reedsolo
  Building wheel for reedsolo (setup.py) ... error
  ERROR: Command errored out with exit status 1:
   command: 'C:\WinPython\WPy64-3860\python-3.8.6.amd64\python.exe' -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\xxxxx\\AppData\\Local\\Temp\\pip-install-sknnm65z\\reedsolo_d0f8e6cce4fe4abfbb92aa3abc1928b7\\setup.py'"'"'; __file__='"'"'C:\\Users\\xxxxx\\AppData\\Local\\Temp\\pip-install-sknnm65z\\reedsolo_d0f8e6cce4fe4abfbb92aa3abc1928b7\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d 'C:\Users\xxxxx\AppData\Local\Temp\pip-wheel-_7lq_i9f'
       cwd: C:\Users\xxxxx\AppData\Local\Temp\pip-install-sknnm65z\reedsolo_d0f8e6cce4fe4abfbb92aa3abc1928b7\
  Complete output (10 lines):
  Cython is installed, building creedsolo module
  running bdist_wheel
  running build
  running build_py
  creating build
  creating build\lib.win-amd64-3.8
  copying reedsolo.py -> build\lib.win-amd64-3.8
  running build_ext
  building 'creedsolo' extension
  error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/
  ----------------------------------------
  ERROR: Failed building wheel for reedsolo
  Running setup.py clean for reedsolo
Failed to build reedsolo
Installing collected packages: reedsolo
    Running setup.py install for reedsolo ... error
    ERROR: Command errored out with exit status 1:
     command: 'C:\WinPython\WPy64-3860\python-3.8.6.amd64\python.exe' -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\xxxxx\\AppData\\Local\\Temp\\pip-install-sknnm65z\\reedsolo_d0f8e6cce4fe4abfbb92aa3abc1928b7\\setup.py'"'"'; __file__='"'"'C:\\Users\\xxxxx\\AppData\\Local\\Temp\\pip-install-sknnm65z\\reedsolo_d0f8e6cce4fe4abfbb92aa3abc1928b7\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record 'C:\Users\xxxxx\AppData\Local\Temp\pip-record-qw0ikq9c\install-record.txt' --single-version-externally-managed --compile --install-headers 'C:\WinPython\WPy64-3860\python-3.8.6.amd64\Include\reedsolo'
         cwd: C:\Users\xxxxx\AppData\Local\Temp\pip-install-sknnm65z\reedsolo_d0f8e6cce4fe4abfbb92aa3abc1928b7\
    Complete output (10 lines):
    Cython is installed, building creedsolo module
    running install
    running build
    running build_py
    creating build
    creating build\lib.win-amd64-3.8
    copying reedsolo.py -> build\lib.win-amd64-3.8
    running build_ext
    building 'creedsolo' extension
    error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/
    ----------------------------------------
ERROR: Command errored out with exit status 1: 'C:\WinPython\WPy64-3860\python-3.8.6.amd64\python.exe' -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\xxxxx\\AppData\\Local\\Temp\\pip-install-sknnm65z\\reedsolo_d0f8e6cce4fe4abfbb92aa3abc1928b7\\setup.py'"'"'; __file__='"'"'C:\\Users\\xxxxx\\AppData\\Local\\Temp\\pip-install-sknnm65z\\reedsolo_d0f8e6cce4fe4abfbb92aa3abc1928b7\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record 'C:\Users\xxxxx\AppData\Local\Temp\pip-record-qw0ikq9c\install-record.txt' --single-version-externally-managed --compile --install-headers 'C:\WinPython\WPy64-3860\python-3.8.6.amd64\Include\reedsolo' Check the logs for full command output.

Expected behaviour

pip installation should run without need to install massive ms toolchain. Provide wheels?

Quick question

So, say I want to use Reed Solomon such as the Voyager Code (255, 223). How would I do that?

2.0.5: pep517 based build fails

It may be result of latest cython upgrade

+ /usr/bin/python3 -sBm build -w --no-isolation -C=--build-option=--cythonize
* Getting build dependencies for wheel...
running egg_info
creating reedsolo.egg-info
writing reedsolo.egg-info/PKG-INFO
writing dependency_links to reedsolo.egg-info/dependency_links.txt
writing top-level names to reedsolo.egg-info/top_level.txt
writing manifest file 'reedsolo.egg-info/SOURCES.txt'
reading manifest file 'reedsolo.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
adding license file 'LICENSE'
writing manifest file 'reedsolo.egg-info/SOURCES.txt'
* Building wheel...
Cython is installed, building creedsolo module
[1/1] Cythonizing creedsolo.pyx

Error compiling Cython file:
------------------------------------------------------------
...
^
------------------------------------------------------------

cython:0:0: cython.cimports is not available
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/pyproject_hooks/_in_process/_in_process.py", line 353, in <module>
    main()
  File "/usr/lib/python3.8/site-packages/pyproject_hooks/_in_process/_in_process.py", line 335, in main
    json_out['return_val'] = hook(**hook_input['kwargs'])
  File "/usr/lib/python3.8/site-packages/pyproject_hooks/_in_process/_in_process.py", line 251, in build_wheel
    return _build_backend().build_wheel(wheel_directory, config_settings,
  File "/usr/lib/python3.8/site-packages/setuptools/build_meta.py", line 413, in build_wheel
    return self._build_with_temp_dir(['bdist_wheel'], '.whl',
  File "/usr/lib/python3.8/site-packages/setuptools/build_meta.py", line 398, in _build_with_temp_dir
    self.run_setup()
  File "/usr/lib/python3.8/site-packages/setuptools/build_meta.py", line 484, in run_setup
    super(_BuildMetaLegacyBackend,
  File "/usr/lib/python3.8/site-packages/setuptools/build_meta.py", line 335, in run_setup
    exec(code, locals())
  File "<string>", line 32, in <module>
  File "/usr/lib64/python3.8/site-packages/Cython/Build/Dependencies.py", line 1115, in cythonize
    cythonize_one(*args)
  File "/usr/lib64/python3.8/site-packages/Cython/Build/Dependencies.py", line 1238, in cythonize_one
    raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: creedsolo.pyx

ERROR Backend subprocess exited when trying to invoke build_wheel

Here is list of installed modules in build env

Package         Version
--------------- -------
build           0.10.0
Cython          0.29.34
distro          1.8.0
exceptiongroup  1.0.0
gpg             1.19.0
iniconfig       2.0.0
installer       0.7.0
libcomps        0.1.19
packaging       23.0
pluggy          1.0.0
pyproject_hooks 1.0.0
pytest          7.3.0
python-dateutil 2.8.2
setuptools      65.6.3
six             1.16.0
tomli           2.0.1
wheel           0.40.0

Encoder returns empty array

This code:

import reedsolo
reedsolo.RSCodec(300).encode(b'a')

Returns bytearray(b'') on my computer. Adjusting the number down causes it to return something.

Inconsistent reporting of error positions when using erasures

The third element of the rs_correct_msg is a list of error positions. The positions returned are inconsistent. Consider the following:

>>> import reedsolo as rs
>>> from reedsolo import rs_encode_msg, rs_correct_msg, rs_check
>>> _ = rs.init_tables()
>>> msg = rs_encode_msg(bytes(range(10)), nsym=4)
>>> msg
bytearray(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\xf0\x9f\x84\xea')
>>> rs_correct_msg(msg, nsym=4, erase_pos=[1])  # CALL 1
(bytearray(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t'), bytearray(b'\xf0\x9f\x84\xea'), [1])
>>> rs_correct_msg(msg, nsym=4, erase_pos=[0])  # CALL 2
(bytearray(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t'), bytearray(b'\xf0\x9f\x84\xea'), [])
>>> msg[0] = 0xFF
>>> rs_correct_msg(msg, nsym=4)  # CALL 3
(bytearray(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t'), bytearray(b'\xf0\x9f\x84\xea'), [0])
>>> rs_correct_msg(msg, nsym=4, erase_pos=[0])  # CALL 4
(bytearray(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t'), bytearray(b'\xf0\x9f\x84\xea'), [])

In call 1, the message is correct, but we mark position 1 as an erasure. The returned bytearrays match the input message, but rs_correct_msg reports position 1 as an error. This is defensible, as that byte was logically erased, even though the correct byte happened to be in the array. But then call 2 should report position 0 as an error.

We then corrupt byte 0 of the message. Call 3 corrects the corruption and reports position 0 as an error (which it is). In call 4 we mark position 0 as an erasure; this call also corrects the corruption, but does not report position 0 as an error.

I think the source of this inconsistency is reedsolo.py lines 728-729, which write 0 to each erased position. In call 1, this corrupts the message, which is subsequently corrected and the error position correctly returned; in call 2, the erased position is already zero, so the message remains correct and rs_correct_msg returns on line 780 with an empty error position list. In call 3, no zeroing happens because no erasures were given, so the message is corrected and the error position correctly returned; in call 4, the zeroing corrects the message and the call returns on line 780 with an empty error position list.

If you agree that all erased positions should be reported as error positions, and you want to keep the zeroing for ease of debugging (according to the comment), the easy fix is to return error_pos on line 780 (in place of the empty list).

Not compatible with matlab's rsenc/rsdec; only deal with n=255.

Hey buddy, as is said in the mail, I tried to improve this package, but encountered an error in the decoder. The improvements to the encoder may help, so I send it to you, hoping that you (or some one else) can (easily) fix it.

p.s. Source code is not permitted to be attached here.

interface to deal with fixed erasures

This is more a feature request. I have a RS encoding that is a bit strange. It uses 8 bytes of error correction, but instead of the expected encoding:

rs8a=reedsolo.RSCodec(nsym=8, fcr=0, prim=0x11d, c_exp=8)

it instead encodes for 16 bytes, but omits the last 8 bytes from the message.

Due to the way that reed-solomon can deal with known erasures it has the same correcting capabilities as the "standard" one.

I have written a small helper function to deal with this case:

# data: 31B, checksum: 8B, erasure: 8B - RS(47,31) - transmission omits last 8B
rs8=reedsolo.RSCodec(nsym=(8+8), fcr=0, prim=0x11d, c_exp=8)
rs8.elen=8

def decode_with_fixed_erasure(self, data, nsym=None, erase_pos=None, only_erasures=False):
    if nsym is None: nsym=self.nsym

    if self.elen > 0:
        data=data+([0]*self.elen)
        r=range(len(data)-self.elen,len(data))
        if erase_pos is None:
            erase_pos=r
        else:
            erase_pos=list(set(r)|set(erase_pos))
        (cmsg,crs,ep)=self.decode(data, nsym, erase_pos=erase_pos, only_erasures=only_erasures)
        return (cmsg, crs, bytearray(set(ep)-set(r)))
    else:
        return self.decode(data, nsym, erase_pos, only_erasures)

I wonder if this is something you would consider adding support for.

Encode parity shards

Hi there!

I'm currently exploring ways to achieve a similar result as the reed-solomon Go library in Python. In the Go library, the encoding process operates on a double array of bytes, which is then passed through the codeSomeShards function to create parity shards.

I'm wondering if it's possible to obtain a similar outcome using Python.
Any suggestions or insights would be greatly appreciated!

creedsolo.pyx fails to cythonize on 2.0.5

No clue what's happening here. 1.7.0 cythonizes just fine. I've tried it in venv too.

$ cython --version
Cython version 0.29.33
$ python --version
Python 3.11.2
$ python setup.py build --cythonize
Cython is installed, building creedsolo module
[1/1] Cythonizing creedsolo.pyx

Error compiling Cython file:
------------------------------------------------------------
...
^
------------------------------------------------------------

cython:0:0: cython.cimports is not available
Traceback (most recent call last):
  File "/tmp/reedsolomon/setup.py", line 32, in <module>
    extensions = cythonize([ Extension('creedsolo', ['creedsolo.pyx']) ], annotate=True, force=True,  # this may fail hard if Cython is installed but there is no C compiler for current Python version, and we have no way to know. Alternatively, we could supply exclude_failures=True , but then for those who really want the cythonized compiled extension, it would be much harder to debug
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/Cython/Build/Dependencies.py", line 1117, in cythonize
    cythonize_one(*args)
  File "/usr/lib/python3.11/site-packages/Cython/Build/Dependencies.py", line 1240, in cythonize_one
    raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: creedsolo.pyx

Questions about two different results with the same coding target.

Thank you for reading this message and thanks to the authors of this reed-solomon Python repository.

If we take (255, 223) coding as an example and assume GF(2^8), the information before coding has 223 bytes, and the reed-solomon ecc has 32 bytes. Hence, the total encoded length is 255 bytes.

First, the information before coding has a file named "input.bin", which has 233 bytes.

input.bin column 1 column 2 column 3 column 4 column 5 column 6 column 7 column 8 column 9 column 10 column 11 column 12 column 13 column 14 column 15 column 16
row 1 0x12 0x34 0x56 0x78 0x90 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99 0x00 0xff
row 2 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
row 3 0x32 0x3a 0x63 0x75 0x3c 0x77 0xb3 0x83 0x86 0x86 0x5c 0x00 0x00 0x20 0x00 0x00
row 4 0x00 0x00 0x20 0x02 0x00 0x10 0x00 0x00 0x01 0x00 0x00 0x00 0xb0 0xf1 0x01 0x00
row 5 0x00 0x00 0x60 0x08 0x00 0x00 0x18 0x30 0x97 0xe5 0x00 0x00 0x53 0xe3 0x0c 0x00
row 6 0x00 0x1a 0xd8 0x28 0xcd 0xe1 0x78 0x10 0x96 0xe5 0x06 0x00 0xa0 0xe1 0x31 0xff
row 7 0x2f 0xe1 0x00 0x20 0x50 0xe2 0xe5 0xff 0xff 0xca 0x05 0x00 0x00 0x0a 0xcc 0x01
row 8 0x9f 0xe5 0x0a 0x10 0xa0 0xe1 0x00 0x00 0x8f 0xe0 0xdc 0xa0 0x90 0xe5 0x01 0x10
row 9 0x41 0xe2 0x94 0x90 0x90 0xe5 0x01 0x20 0x02 0xe0 0x00 0x30 0xa0 0xe3 0xc8 0xd0
row10 0x4d 0xe2 0x03 0x00 0x92 0xe1 0x03 0x00 0x00 0x0a 0x50 0x03 0x9f 0xe5 0x00 0x00
row11 0x8f 0xe0 0xa0 0x90 0xe5 0x01 0x10 0x41 0xe2 0x94 0x90 0x90 0xe5 0x01 0x20 0x02
row12 0xe0 0x00 0x30 0xa0 0xe3 0xc8 0xd0 0x4d 0xe2 0x03 0x00 0x92 0xe1 0x03 0x08 0x10
row13 0x9d 0xe5 0x00 0x00 0x51 0xe3 0x04 0x00 0x00 0x0a 0x08 0x20 0x9d 0xe5 0x04 0x00
row14 0xa0 0x08 0x10 0x9d 0xe5 0x00 0x00 0x51 0xe3 0x04 0x00 0x00 0x0a 0x08 0x20 -

Second, I use the MATLAB to generate the RS code, and the result of 32-byte RS code is below. Please note that this 32-byte RS code generated by MATLAB is my desired result. However, the Python script provided by his Github repository has different results (see the third part).

m = 8;
n = 255;
k = 223;
prim_poly = 285;
genpoly = rsgenpoly(n,k,prim_poly,1);
fp = fopen('input.bin','r','b') ;
data = fread(fp, 'ubit8')';
info_bytes = gf(data ,m);
encode_bytes = rsenc(info_bytes,n,k,genpoly) ;
encode_bytes.x %print this, then see the 255 bytes with rs code

rs code column 1 column 2 column 3 column 4 column 5 column 6 column 7 column 8 column 9 column 10 column 11 column 12 column 13 column 14 column 15 column 16
row 1 0x28 0x49 0x62 0x92 0x1d 0x89 0x5c 0x81 0x02 0xde 0x7b 0x1f 0xb3 0xc1 0xa8 0x48
row 2 0xd4 0xe9 0xad 0x47 0xf4 0x70 0x58 0x8b 0x97 0x1d 0x8a 0x83 0x42 0x32 0x29 0x7d

Third, I use the Python script provided by his Github repository.

from reedsolo import RSCodec
import reedsolo as rs
inputfile = open('input.bin', "rb")
n = 255
rsc = RSCodec(32)
rs.init_tables(0x11d)
ecc_encode = rsc.encode(inputfile.read())

If you print ecc_encode out, you can see the 32-byte RS code below.

rs code column 1 column 2 column 3 column 4 column 5 column 6 column 7 column 8 column 9 column 10 column 11 column 12 column 13 column 14 column 15 column 16
row 1 0xfd 0xe2 0xaa 0x47 0x93 0xad 0x57 0x5b 0xca 0xd8 0x17 0x19 0xe2 0x38 0x52 0x7d
row 2 0x74 0xa3 0x98 0xf9 0x23 0xc3 0xa3 0xba 0x3d 0x80 0x80 0x02 0x61 0xf5 0x91 0x29

The result generated by Python script is not corrected (at least, it is not my desired).

Could you help me correct the Python script? Thank you.

How to correct errors with `creedsolo` (`2.1.2b1`)?

Hello!
I'm having issues with using the cythonized creedsolo, encoding and checking seems to work great, but my attempt to correct errors results in a ReedSolomonError:

FAILED test.py::test_reed_solomon - creedsolo.creedsolo.ReedSolomonError: Too many (or few) errors found by Chien Search for the errata locator polynomial!

(It works as expected with the pure Python version.)

My test case used:

# Pure Python works as expected
# import reedsolo as rs
import creedsolo as rs

DATA = b'123456789abcdef\n' * 64


def test_reed_solomon():
    prim = rs.find_prime_polys(c_exp=11, fast_primes=True, single=True)[0]
    rs.init_tables(c_exp=11, prim=prim)

    gen = rs.rs_generator_poly_all(1024+16)
    encoded = rs.rs_encode_msg(DATA, 16, gen=gen[16])

    encoded[3] = ord('7')
    encoded[100] = ord('!')

    assert not rs.rs_check(encoded, 16)
    _, _, errata_pos = rs.rs_correct_msg(encoded, 16)
    assert set(errata_pos) == {3, 100}

FWIW, I used the current Git master @ 23b8eab

pip install . --config-setting="--build-option=--cythonize"

Question about Performance

From your Readme:

The codec has quite reasonable performances if you either use PyPy on the pure-python implementation (reedsolo.py) or either if you compile the Cython extension creedsolo.pyx (which is about 2x faster than PyPy). You can expect encoding rates of several MB/s.

Is this still valid?
I just did a performance evaluation for three different python versions (3.7, 3.8, 3.9) with the following code.

import sys
import numpy as np
from reedsolo import RSCodec
import perfplot

name = f'perf_v{sys.version_info.major}.{sys.version_info.minor}'

def func(rscoder, array):
    enc = rscoder.encode(array)
    return rscoder.decode(enc)[0]

codecs = [
    RSCodec(8),
    RSCodec(16),
]

out = perfplot.bench(
    setup = lambda n: np.random.randint(0,255,size=n, dtype=np.uint8),
    kernels = [
        lambda a: func(codec, a) for codec in codecs
    ],
    labels = [codec.nsym for codec in codecs],
    n_range = [2 ** k for k in range(20)],
)
out.show()
out.save(name + ".png", transparent=True, bbox_inches="tight")

I get a maximum of 100kB/s (for encoding and decoding together).

Is this an expected speed? Tested on Ubuntu 20.04, cython is installed.

Here is the output of the three perfplots.

perf_v3 7
perf_v3 8
perf_v3 9

I expected the cythonized function to be faster. However the number of ecc symobls seems to be not relevant here.

PS:
To reproduce with python3.7, you'll have to install perfplot==0.9.6

Error while installing : legacy-install-failure

Hi, I am trying to install esptool and reedsolo is a dependency however installation with pip fails with the following error message

Building wheels for collected packages: reedsolo
  Building wheel for reedsolo (setup.py) ... error
  error: subprocess-exited-with-error
  
  × python setup.py bdist_wheel did not run successfully.
  │ exit code: 1
  ╰─> [124 lines of output]
      Cython is installed, building creedsolo module
      running bdist_wheel
      running build
      running build_py
      creating build
      creating build/lib.linux-x86_64-cpython-310
      copying reedsolo.py -> build/lib.linux-x86_64-cpython-310
      running build_ext
      building 'creedsolo' extension
      creating build/temp.linux-x86_64-cpython-310
      gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug/python -flto=auto -ffat-lto-objects -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug/python -flto=auto -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug/python -flto=auto -fPIC -I/usr/include/python3.10 -c creedsolo.c -o build/temp.linux-x86_64-cpython-310/creedsolo.o
      creedsolo.c: In function ‘__pyx_tp_dealloc_array’:
      creedsolo.c:32027:5: error: lvalue required as increment operand
      32027 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32029:5: error: lvalue required as decrement operand
      32029 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__pyx_tp_dealloc_memoryview’:
      creedsolo.c:32338:5: error: lvalue required as increment operand
      32338 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32340:5: error: lvalue required as decrement operand
      32340 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__pyx_tp_dealloc__memoryviewslice’:
      creedsolo.c:32588:5: error: lvalue required as increment operand
      32588 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32590:5: error: lvalue required as decrement operand
      32590 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__Pyx_InitGlobals’:
      creedsolo.c:33787:1: warning: ‘PyEval_InitThreads’ is deprecated [-Wdeprecated-declarations]
      33787 | PyEval_InitThreads();
            | ^~~~~~~~~~~~~~~~~~
      In file included from /usr/include/python3.10/Python.h:130,
                       from creedsolo.c:16:
      /usr/include/python3.10/ceval.h:122:37: note: declared here
        122 | Py_DEPRECATED(3.9) PyAPI_FUNC(void) PyEval_InitThreads(void);
            |                                     ^~~~~~~~~~~~~~~~~~
      creedsolo.c: In function ‘__Pyx_ParseOptionalKeywords’:
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      In file included from /usr/include/python3.10/unicodeobject.h:1046,
                       from /usr/include/python3.10/Python.h:83:
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c: In function ‘__Pyx_decode_c_string’:
      creedsolo.c:37955:9: warning: ‘PyUnicode_FromUnicode’ is deprecated [-Wdeprecated-declarations]
      37955 |         return PyUnicode_FromUnicode(NULL, 0);
            |         ^~~~~~
      /usr/include/python3.10/cpython/unicodeobject.h:551:42: note: declared here
        551 | Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_FromUnicode(
            |                                          ^~~~~~~~~~~~~~~~~~~~~
      error: command '/usr/bin/gcc' failed with exit code 1
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for reedsolo
  Running setup.py clean for reedsolo
Failed to build reedsolo
Installing collected packages: reedsolo, bitstring, ecdsa, esptool
  Running setup.py install for reedsolo ... error
  error: subprocess-exited-with-error
  
  × Running setup.py install for reedsolo did not run successfully.
  │ exit code: 1
  ╰─> [126 lines of output]
      Cython is installed, building creedsolo module
      running install
      /home/sbn/.local/lib/python3.10/site-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
        warnings.warn(
      running build
      running build_py
      creating build
      creating build/lib.linux-x86_64-cpython-310
      copying reedsolo.py -> build/lib.linux-x86_64-cpython-310
      running build_ext
      building 'creedsolo' extension
      creating build/temp.linux-x86_64-cpython-310
      gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug/python -flto=auto -ffat-lto-objects -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug/python -flto=auto -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/python/src=/usr/src/debug/python -flto=auto -fPIC -I/usr/include/python3.10 -c creedsolo.c -o build/temp.linux-x86_64-cpython-310/creedsolo.o
      creedsolo.c: In function ‘__pyx_tp_dealloc_array’:
      creedsolo.c:32027:5: error: lvalue required as increment operand
      32027 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32029:5: error: lvalue required as decrement operand
      32029 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__pyx_tp_dealloc_memoryview’:
      creedsolo.c:32338:5: error: lvalue required as increment operand
      32338 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32340:5: error: lvalue required as decrement operand
      32340 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__pyx_tp_dealloc__memoryviewslice’:
      creedsolo.c:32588:5: error: lvalue required as increment operand
      32588 |     ++Py_REFCNT(o);
            |     ^~
      creedsolo.c:32590:5: error: lvalue required as decrement operand
      32590 |     --Py_REFCNT(o);
            |     ^~
      creedsolo.c: In function ‘__Pyx_InitGlobals’:
      creedsolo.c:33787:1: warning: ‘PyEval_InitThreads’ is deprecated [-Wdeprecated-declarations]
      33787 | PyEval_InitThreads();
            | ^~~~~~~~~~~~~~~~~~
      In file included from /usr/include/python3.10/Python.h:130,
                       from creedsolo.c:16:
      /usr/include/python3.10/ceval.h:122:37: note: declared here
        122 | Py_DEPRECATED(3.9) PyAPI_FUNC(void) PyEval_InitThreads(void);
            |                                     ^~~~~~~~~~~~~~~~~~
      creedsolo.c: In function ‘__Pyx_ParseOptionalKeywords’:
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      In file included from /usr/include/python3.10/unicodeobject.h:1046,
                       from /usr/include/python3.10/Python.h:83:
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35153:21: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35153 |                     (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                     ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘PyUnicode_AsUnicode’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:580:45: note: declared here
        580 | Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
            |                                             ^~~~~~~~~~~~~~~~~~~
      creedsolo.c:35169:25: warning: ‘_PyUnicode_get_wstr_length’ is deprecated [-Wdeprecated-declarations]
      35169 |                         (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
            |                         ^
      /usr/include/python3.10/cpython/unicodeobject.h:446:26: note: declared here
        446 | static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) {
            |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
      creedsolo.c: In function ‘__Pyx_decode_c_string’:
      creedsolo.c:37955:9: warning: ‘PyUnicode_FromUnicode’ is deprecated [-Wdeprecated-declarations]
      37955 |         return PyUnicode_FromUnicode(NULL, 0);
            |         ^~~~~~
      /usr/include/python3.10/cpython/unicodeobject.h:551:42: note: declared here
        551 | Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_FromUnicode(
            |                                          ^~~~~~~~~~~~~~~~~~~~~
      error: command '/usr/bin/gcc' failed with exit code 1
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
error: legacy-install-failure

× Encountered error while trying to install package.
╰─> reedsolo

Please help me solve this issue.
Thanks

Binary codes using RSCodec

Is it possible to use RSCodec as a binary codec? If so, how do I do that? I've tried limiting the symbol alphabet by setting c_exp to 2 (edit: *set c_exp to 1) only to be greeted with this error message:
File "<stdin>", line 1, in <module> File "/opt/homebrew/lib/python3.9/site-packages/reedsolo.py", line 869, in __init__ self.gen[nsym] = rs_generator_poly(nsym, fcr=fcr, generator=generator) File "/opt/homebrew/lib/python3.9/site-packages/reedsolo.py", line 484, in rs_generator_poly g = gf_poly_mul(g, [1, gf_pow(generator, i+fcr)]) File "/opt/homebrew/lib/python3.9/site-packages/reedsolo.py", line 331, in gf_pow return gf_exp[(gf_log[x] * power) % field_charac] IndexError: bytearray index out of range

I think someone has attempted to do the same thing here.

Have some issues with decoding

I'm trying this library with error correcting on network sent messages (between two VMs), but something is wrong. When network connection is perfect and there's no packet loss encoding and decoding works perfectly, but when I'm simulating packet loss even with 4 or 5 errors I get error message:

File "/usr/local/lib/python3.8/dist-packages/reedsolo.py", line 924, in decode
    rmes, recc, errata_pos = rs_correct_msg(chunk, nsym, fcr=self.fcr, generator=self.generator, erase_pos=e_pos, only_erasures=only_erasures)
File "/usr/local/lib/python3.8/dist-packages/reedsolo.py", line 745, in rs_correct_msg
    err_loc = rs_find_error_locator(fsynd, nsym, erase_count=len(erase_pos))
File "/usr/local/lib/python3.8/dist-packages/reedsolo.py", line 658, in rs_find_error_locator
    raise ReedSolomonError("Too many errors to correct")

I'm working with ECC=13 and I even track errors and their positions which I enter in decoding and still.. Any ideas?
EDIT:
Got this:

Message sent: bytearray(b'123456rx\x15\xa5\x81\x88\xab\xd5\x0c\xf1E\xbc\xce')

Message received:
[14]
bytearray(b'123456'), bytearray(b'123456rx\x15\xa5\x81\x88\xab\xd5\x0c\xf1E\xbc\xce'), bytearray(b'\x12\x11\x10\x0f\x0e\r')

[14] - is error position by my calculation (14th byte)

EDIT No. 2:
My bad, sorry :) Kinda works, though, error positions that my code shows and reedsolo show still aren't identical

C implementation doesn't work with larger messages.

I'm using the following code:

import creedsolo as rs
prim = rs.find_prime_polys(c_exp=12, fast_primes=False, single=True)
rs.init_tables(prim=prim, c_exp=12)

The error thrown is:

    c_gf_log, c_gf_exp, c_field_charac = rs.init_tables(prim=prim, c_exp=12)
  File "creedsolo.pyx", line 203, in creedsolo.init_tables
OverflowError: value too large to convert to unsigned char

I'm trying to encode a file with 4095 sized chunks without using the additional 255 internal splittings. I've precalculated the polynomial numbers. So far I was able to encode, but not to decode the message:

import creedsolo as rs
rs.init_tables()

mesecc = rs.rs_encode_msg(data, nsym, gen=gen[nsym]) # nsym = 123, len(data) = 3972
rmes, rmesecc, errata_pos = rs.rs_correct_msg(mesecc, nsym)

The error thrown is:

    rmes, rmesecc, errata_pos = rs.rs_correct_msg(mesecc, nsym)
  File "creedsolo.pyx", line 694, in creedsolo.rs_correct_msg
ValueError: Message is too long (4095 when max is 255)

Is there a way to encode 4095 chunks without hidden splitting?

Code does not work with an input of class 'bytes' when c_exp > 8

example code:

    import reedsolo
    rsc = reedsolo.RSCodec(12, c_exp=12) # same as nsize=4095
    str_msg = "This is a message"
    bytes_msg = b"This is a binary message"
    breakpoint()
    result = rsc.encode(str_msg) # this works
    result_b = rsc.encode(bytes_msg) # this fails

Error message:

Traceback (most recent call last):
  File "rs_tensor.py", line 120, in <module>
    result_b = rsc.encode(bytes_msg) # this fails
  File "reedsolo.py", line 893, in encode
    enc.extend(rs_encode_msg(chunk, self.nsym, fcr=self.fcr, generator=self.generator, gen=self.gen[nsym]))
  File "reedsolo.py", line 526, in rs_encode_msg
    lcoef = gf_log[coef] # precaching
IndexError: array index out of range

The problem seems to stem from the fact that the code in _bytearray has the following fall-through code (if obj is not a str or int)

278             # Else obj is a list of int, it's ok
279             return array("i", obj)

When in reality, obj is of type bytes.

Proposed fix:

        def _bytearray(obj = 0, encoding = "latin-1"):
            '''Fake bytearray replacement, supporting int values above 255'''
            # always use Latin-1 and not UTF8 because Latin-1 maps the first 256 characters to their bytevalue equivalents. UTF8 may mangle your data (particularly at vale 128)
            if isinstance(obj, str):  # obj is a string, convert to list of ints
                obj = obj.encode(encoding)
                if isinstance(obj, str):  # Py2 str: convert to list of ascii ints
                    obj = [ord(chr) for chr in obj]
                elif isinstance(obj, bytes):  # Py3 bytes: characters are bytes, need to convert to int for array.array('i', obj)
                    obj = [int(chr) for chr in obj]
                else:
                    raise(ValueError, "Type of object not recognized!")
            elif isinstance(obj, int):  # compatibility with list preallocation bytearray(int)
                obj = [0] * obj
            elif isinstance(obj, bytes):
                obj = [int(b) for b in obj]
            # Else obj is a list of int, it's ok
            return array("i", obj)

Decoding a message composed of 4-bit words in GF(16)

Hi, I'm trying to use this module to verify the so-called "mode message" of Aztec codes (see: https://en.wikipedia.org/wiki/Aztec_Code#Mode_message)
Reed Solomon is used on GF(16), using the polynomial x^4 + x + 1 (= 0x13, 10011b, 19 decimal). The message is composed of 7 words, two 4-bit data words followed by five 4-bit ECC words.

The reference Aztec code from the specification has the following mode message: [0, 9, 12, 2, 3, 1, 9] (0000100111000010001100011001 in binary), which I just can't get to decode, and I don't understand why.

My verification code:

>>> from reedsolo import RSCodec, ReedSolomonError
>>> words = [0, 9, 12, 2, 3, 1, 9]
>>> prim = 19  # 10011 = x^4 + x^1 + x^0
>>> c_exp = 4  # 2^4 = GF(16)
>>> nsym = 5 # number of ECC words for compat Aztec codes
>>> rsc = RSCodec(nsym=nsym, c_exp=c_exp, prim=prim)
>>> rsc.decode(words)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File ".../venv/lib/python3.10/site-packages/reedsolo.py", line 929, in decode
    rmes, recc, errata_pos = rs_correct_msg(chunk, nsym, fcr=self.fcr, generator=self.generator, erase_pos=e_pos, only_erasures=only_erasures)
  File ".../venv/lib/python3.10/site-packages/reedsolo.py", line 760, in rs_correct_msg
    raise ReedSolomonError("Could not correct message")
reedsolo.ReedSolomonError: Could not correct message

I also checked another implementation (zxing) and they use the same RS parameters as I do ( https://github.com/zxing-cpp/zxing-cpp/blob/master/core/src/GenericGF.cpp#L33), and there the same message decodes correctly there (0 errors).

Can someone tell me where I'm wrong? I just started digging into Reed-Solomon codes so it might very well be a fundamental misunderstanding on my side. :-)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.