Repository: mersinvald/Reed-Solomon Branch: master Commit: 96b9a08d245b Files: 22 Total size: 45.8 KB Directory structure: gitextract_c0tv96n1/ ├── .gitexcludes ├── .github/ │ └── workflows/ │ └── codeql-analysis.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── examples/ │ ├── CMakeLists.txt │ └── example1.cpp ├── include/ │ ├── gf.hpp │ ├── poly.hpp │ └── rs.hpp ├── tests/ │ ├── CMakeLists.txt │ ├── gftest.cpp │ ├── gftest.hpp │ ├── performancetest.cpp │ ├── performancetest.hpp │ ├── rstest.cpp │ ├── rstest.hpp │ └── tests.cpp └── x32.cmake_toolchain ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitexcludes ================================================ ./src/libRS.pro.user ./tests/tests.pro.user ./Reed-Solomon.pro.user ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. name: "CodeQL" on: push: branches: [master] pull_request: # The branches below must be a subset of the branches above branches: [master] schedule: - cron: '0 12 * * 2' jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] language: ['cpp'] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection steps: - name: Checkout repository uses: actions/checkout@v2 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. fetch-depth: 2 # If this run was triggered by a pull request event, then checkout # the head of the pull request instead of the merge commit. - run: git checkout HEAD^2 if: ${{ github.event_name == 'pull_request' }} # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 ================================================ FILE: .gitignore ================================================ *.so *.a *.o ================================================ FILE: .gitmodules ================================================ [submodule "tests/mtest"] path = tests/mtest url = git@github.com:mersinvald/mtest.git ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 2.8.10) project(Reed-Solomon) # You can tweak some common (for all subprojects) stuff here. For example: set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) set(CMAKE_DISABLE_SOURCE_CHANGES ON) if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") message(SEND_ERROR "In-source builds are not allowed.") endif () set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_COLOR_MAKEFILE ON) # Remove 'lib' prefix for shared libraries on Windows if (WIN32) set(CMAKE_SHARED_LIBRARY_PREFIX "") endif () set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") set(RS_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include) # When done tweaking common stuff, configure the components (subprojects). # NOTE: The order matters! The most independent ones should go first. add_subdirectory(examples) add_subdirectory(tests) ================================================ FILE: LICENSE ================================================ Copyright © 2015 Mike Lubinets, github.com/mersinvald Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ all: mkdir -p build && cd build && cmake ../ && make x32: mkdir -p build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=../x32.cmake_toolchain ../ && make clean: cd build && make clean && cd .. && rm -rf build ================================================ FILE: README.md ================================================ # Reed-Solomon Reed Solomon BCH encoder and decoder library ## Overview This RS implementation was designed for embedded purposes, so all the memory allocations performed on the stack.
If somebody wants to reimplement memory management with heap usage, pull requests are welcome ## Getting the source If you want only Reed-Solomon code, just clone repository.
If you want to get tests and examples also, do ``` git clone --recursive git@github.com:mersinvald/Reed-Solomon.git ``` ## Build There is no need in building RS library, cause all the implementation is in headers.
To build tests and examples simply run make in the folder with cloned repo and executables will emerge in the ./build folder ## Usage All the Reed-Solomon code is in folder **include**, you just need to include header rs.hpp Template class ReedSolomon accepts two template arguments: message length and ecc length.
Simple example:
``` char message[] = "Some very important message ought to be delivered"; const int msglen = sizeof(message); const int ecclen = 8; char repaired[msglen]; char encoded[msglen + ecclen]; RS::ReedSolomon rs; rs.Encode(message, encoded); // Corrupting first 8 bytes of message (any 4 bytes can be repaired, no more) for(uint i = 0; i < ecclen / 2; i++) { encoded[i] = 'E'; } rs.Decode(encoded, repaired); std::cout << "Original: " << message << std::endl; std::cout << "Corrupted: " << encoded << std::endl; std::cout << "Repaired: " << repaired << std::endl; std::cout << ((memcmp(message, repaired, msglen) == 0) ? "SUCCESS" : "FAILURE") << std::endl; ``` ## Regards Huge thanks to authors of [wikiversity page about Reed-Solomon BCH](https://en.wikiversity.org/wiki/Reed–Solomon_codes_for_coders) ## Related projects [Arduino Reed-Solomon Forward Error Correction library](https://github.com/simonyipeter/Arduino-FEC) ================================================ FILE: examples/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 2.8.10) project(examples CXX) include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) if(COMPILER_SUPPORTS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") elseif(COMPILER_SUPPORTS_CXX0X) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") else() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") endif() include_directories(${RS_INCLUDE_DIRS}) add_executable(example1 example1.cpp) ================================================ FILE: examples/example1.cpp ================================================ /* Author: Mike Lubinets (aka mersinvald) * Date: 29.12.15 * * See LICENSE */ #include #include "rs.hpp" using namespace std; #define ECC_LENGTH 8 int main() { char message[] = "Some very important message ought to be delivered"; const int msglen = sizeof(message); char repaired[msglen]; char encoded[msglen + ECC_LENGTH]; RS::ReedSolomon rs; rs.Encode(message, encoded); // Corrupting first 8 bytes of message (any 8 bytes can be repaired) for(uint i = 0; i < ECC_LENGTH / 2; i++) { encoded[i] = 'E'; } rs.Decode(encoded, repaired); std::cout << "Original: " << message << std::endl; std::cout << "Corrupted: " << encoded << std::endl; std::cout << "Repaired: " << repaired << std::endl; std::cout << ((memcmp(message, repaired, msglen) == 0) ? "SUCCESS" : "FAILURE") << std::endl; return 0; } ================================================ FILE: include/gf.hpp ================================================ /* Author: Mike Lubinets (aka mersinvald) * Date: 29.12.15 * * See LICENSE */ #ifndef GF_H #define GF_H #include #include #include "poly.hpp" #if !defined RS_DEBUG && !defined __CC_ARM && !defined RS_NO_ASSERT #include #else #define assert(dummy) #endif namespace RS { namespace gf { /* GF tables pre-calculated for 0x11d primitive polynomial */ const uint8_t exp[255] = { 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x3, 0x6, 0xc, 0x18, 0x30, 0x60, 0xc0, 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 0x46, 0x8c, 0x5, 0xa, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0xf, 0x1e, 0x3c, 0x78, 0xf0, 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0xd, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x7, 0xe, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x9, 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0xb, 0x16, 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e }; const uint8_t log[256] = { 0x0, 0x0, 0x1, 0x19, 0x2, 0x32, 0x1a, 0xc6, 0x3, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b, 0x4, 0x64, 0xe0, 0xe, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x8, 0x4c, 0x71, 0x5, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0xf, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45, 0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x9, 0x78, 0x4d, 0xe4, 0x72, 0xa6, 0x6, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88, 0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40, 0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d, 0xca, 0x5e, 0x9b, 0x9f, 0xa, 0x15, 0x79, 0x2b, 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57, 0x7, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0xd, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18, 0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e, 0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61, 0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2, 0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0xc, 0x6f, 0xf6, 0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a, 0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0xb, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7, 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf }; /* ################################ * # OPERATIONS OVER GALOIS FIELDS # * ################################ */ /* @brief Addition in Galois Fields * @param x - left operand * @param y - right operand * @return x + y */ inline uint8_t add(uint8_t x, uint8_t y) { return x^y; } /* ##### GF subtraction ###### */ /* @brief Subtraction in Galois Fields * @param x - left operand * @param y - right operand * @return x - y */ inline uint8_t sub(uint8_t x, uint8_t y) { return x^y; } /* @brief Multiplication in Galois Fields * @param x - left operand * @param y - right operand * @return x * y */ inline uint8_t mul(uint16_t x, uint16_t y){ if (x == 0 || y == 0) return 0; return exp[(log[x] + log[y]) % 255]; } /* @brief Division in Galois Fields * @param x - dividend * @param y - divisor * @return x / y */ inline uint8_t div(uint8_t x, uint8_t y){ assert(y != 0); if(x == 0) return 0; return exp[(log[x] + 255 - log[y]) % 255]; } /* @brief X in power Y w * @param x - operand * @param power - power * @return x^power */ inline uint8_t pow(uint8_t x, intmax_t power){ intmax_t i = log[x]; i *= power; i %= 255; if(i < 0) i = i + 255; return exp[i % 255]; } /* @brief Inversion in Galois Fields * @param x - number * @return inversion of x */ inline uint8_t inverse(uint8_t x){ return exp[(255 - log[x]) % 255]; /* == div(1, x); */ } /* ########################## * # POLYNOMIALS OPERATIONS # * ########################## */ /* @brief Multiplication polynomial by scalar * @param &p - source polynomial * @param &newp - destination polynomial * @param x - scalar */ inline void poly_scale(const Poly *p, Poly *newp, uint16_t x) { newp->length = p->length; for(uint16_t i = 0; i < p->length; i++){ newp->at(i) = mul(p->at(i), x); } } /* @brief Addition of two polynomials * @param &p - right operand polynomial * @param &q - left operand polynomial * @param &newp - destination polynomial */ inline void poly_add(const Poly *p, const Poly *q, Poly *newp) { newp->length = poly_max(p->length, q->length); memset(newp->ptr(), 0, newp->length * sizeof(uint8_t)); for(uint8_t i = 0; i < p->length; i++){ newp->at(i + newp->length - p->length) = p->at(i); } for(uint8_t i = 0; i < q->length; i++){ newp->at(i + newp->length - q->length) ^= q->at(i); } } /* @brief Multiplication of two polynomials * @param &p - right operand polynomial * @param &q - left operand polynomial * @param &newp - destination polynomial */ inline void poly_mul(const Poly *p, const Poly *q, Poly *newp) { newp->length = p->length + q->length - 1; memset(newp->ptr(), 0, newp->length * sizeof(uint8_t)); /* Compute the polynomial multiplication (just like the outer product of two vectors, * we multiply each coefficients of p with all coefficients of q) */ for(uint8_t j = 0; j < q->length; j++){ for(uint8_t i = 0; i < p->length; i++){ newp->at(i+j) ^= mul(p->at(i), q->at(j)); /* == r[i + j] = gf_add(r[i+j], gf_mul(p[i], q[j])) */ } } } /* @brief Division of two polynomials * @param &p - right operand polynomial * @param &q - left operand polynomial * @param &newp - destination polynomial */ inline void poly_div(const Poly *p, const Poly *q, Poly *newp) { if(p->ptr() != newp->ptr()) { memcpy(newp->ptr(), p->ptr(), p->length*sizeof(uint8_t)); } newp->length = p->length; uint8_t coef; for(int i = 0; i < (p->length-(q->length-1)); i++){ coef = newp->at(i); if(coef != 0){ for(uint8_t j = 1; j < q->length; j++){ if(q->at(j) != 0) newp->at(i+j) ^= mul(q->at(j), coef); } } } size_t sep = p->length-(q->length-1); memmove(newp->ptr(), newp->ptr()+sep, (newp->length-sep) * sizeof(uint8_t)); newp->length = newp->length-sep; } /* @brief Evaluation of polynomial in x * @param &p - polynomial to evaluate * @param x - evaluation point */ inline int8_t poly_eval(const Poly *p, uint16_t x) { uint8_t y = p->at(0); for(uint8_t i = 1; i < p->length; i++){ y = mul(y, x) ^ p->at(i); } return y; } } /* end of gf namespace */ } #endif // GF_H ================================================ FILE: include/poly.hpp ================================================ /* Author: Mike Lubinets (aka mersinvald) * Date: 29.12.15 * * See LICENSE */ #ifndef POLY_H #define POLY_H #include #include #if !defined RS_DEBUG && !defined __CC_ARM && !defined RS_NO_ASSERT #include #else #define assert(dummy) #endif namespace RS { struct Poly { Poly() : length(0), _memory(NULL) {} Poly(uint8_t id, uint16_t offset, uint8_t size) \ : length(0), _id(id), _size(size), _offset(offset), _memory(NULL) {} /* @brief Append number at the end of polynomial * @param num - number to append * @return false if polynomial can't be stretched */ inline bool Append(uint8_t num) { assert(length+1 < _size); ptr()[length++] = num; return true; } /* @brief Polynomial initialization */ inline void Init(uint8_t id, uint16_t offset, uint8_t size, uint8_t** memory_ptr) { this->_id = id; this->_offset = offset; this->_size = size; this->length = 0; this->_memory = memory_ptr; } /* @brief Polynomial memory zeroing */ inline void Reset() { memset((void*)ptr(), 0, this->_size); } /* @brief Copy polynomial to memory * @param src - source byte-sequence * @param size - size of polynomial * @param offset - write offset */ inline void Set(const uint8_t* src, uint8_t len, uint8_t offset = 0) { assert(src && len <= this->_size-offset); memcpy(ptr()+offset, src, len * sizeof(uint8_t)); length = len + offset; } #define poly_max(a, b) ((a > b) ? (a) : (b)) inline void Copy(const Poly* src) { length = poly_max(length, src->length); Set(src->ptr(), length); } inline uint8_t& at(uint8_t i) const { assert(i < _size); return ptr()[i]; } inline uint8_t id() const { return _id; } inline uint8_t size() const { return _size; } // Returns pointer to memory of this polynomial inline uint8_t* ptr() const { assert(_memory && *_memory); return (*_memory) + _offset; } uint8_t length; protected: uint8_t _id; uint8_t _size; // Size of reserved memory for this polynomial uint16_t _offset; // Offset in memory uint8_t** _memory; // Pointer to pointer to memory }; } #endif // POLY_H ================================================ FILE: include/rs.hpp ================================================ /* Author: Mike Lubinets (aka mersinvald) * Date: 29.12.15 * * See LICENSE */ #ifndef RS_HPP #define RS_HPP #include #include #include "poly.hpp" #include "gf.hpp" #if !defined RS_DEBUG && !defined __CC_ARM && !defined RS_NO_ASSERT #include #else #define assert(dummy) #endif namespace RS { #define MSG_CNT 3 // message-length polynomials count #define POLY_CNT 14 // (ecc_length*2)-length polynomials count template // Length of correction code class ReedSolomon { public: ReedSolomon() { const uint8_t enc_len = msg_length + ecc_length; const uint8_t poly_len = ecc_length * 2; uint8_t** memptr = &memory; uint16_t offset = 0; /* Initialize first six polys manually cause their amount depends on template parameters */ polynoms[0].Init(ID_MSG_IN, offset, enc_len, memptr); offset += enc_len; polynoms[1].Init(ID_MSG_OUT, offset, enc_len, memptr); offset += enc_len; for(uint8_t i = ID_GENERATOR; i < ID_MSG_E; i++) { polynoms[i].Init(i, offset, poly_len, memptr); offset += poly_len; } polynoms[5].Init(ID_MSG_E, offset, enc_len, memptr); offset += enc_len; for(uint8_t i = ID_TPOLY3; i < ID_ERR_EVAL+2; i++) { polynoms[i].Init(i, offset, poly_len, memptr); offset += poly_len; } } ~ReedSolomon() { // Dummy destructor, gcc-generated one crashes program memory = NULL; } /* @brief Message block encoding * @param *src - input message buffer (msg_length size) * @param *dst - output buffer for ecc (ecc_length size at least) */ void EncodeBlock(const void* src, void* dst) { assert(msg_length + ecc_length < 256); /* Generator cache, it dosn't change for one template parameters */ static uint8_t generator_cache[ecc_length+1] = {0}; static bool generator_cached = false; /* Allocating memory on stack for polynomials storage */ uint8_t stack_memory[MSG_CNT * msg_length + POLY_CNT * ecc_length * 2]; this->memory = stack_memory; const uint8_t* src_ptr = (const uint8_t*) src; uint8_t* dst_ptr = (uint8_t*) dst; Poly *msg_in = &polynoms[ID_MSG_IN]; Poly *msg_out = &polynoms[ID_MSG_OUT]; Poly *gen = &polynoms[ID_GENERATOR]; // Weird shit, but without resetting msg_in it simply doesn't work msg_in->Reset(); msg_out->Reset(); // Using cached generator or generating new one if(generator_cached) { gen->Set(generator_cache, sizeof(generator_cache)); } else { GeneratorPoly(); memcpy(generator_cache, gen->ptr(), gen->length); generator_cached = true; } // Copying input message to internal polynomial msg_in->Set(src_ptr, msg_length); msg_out->Set(src_ptr, msg_length); msg_out->length = msg_in->length + ecc_length; // Here all the magic happens uint8_t coef = 0; // cache for(uint8_t i = 0; i < msg_length; i++){ coef = msg_out->at(i); if(coef != 0){ for(uint32_t j = 1; j < gen->length; j++){ msg_out->at(i+j) ^= gf::mul(gen->at(j), coef); } } } // Copying ECC to the output buffer memcpy(dst_ptr, msg_out->ptr()+msg_length, ecc_length * sizeof(uint8_t)); } /* @brief Message encoding * @param *src - input message buffer (msg_length size) * @param *dst - output buffer (msg_length + ecc_length size at least) */ void Encode(const void* src, void* dst) { uint8_t* dst_ptr = (uint8_t*) dst; // Copying message to the output buffer memcpy(dst_ptr, src, msg_length * sizeof(uint8_t)); // Calling EncodeBlock to write ecc to out[ut buffer EncodeBlock(src, dst_ptr+msg_length); } /* @brief Message block decoding * @param *src - encoded message buffer (msg_length size) * @param *ecc - ecc buffer (ecc_length size) * @param *msg_out - output buffer (msg_length size at least) * @param *erase_pos - known errors positions * @param erase_count - count of known errors * @return RESULT_SUCCESS if successful, error code otherwise */ int DecodeBlock(const void* src, const void* ecc, void* dst, uint8_t* erase_pos = NULL, size_t erase_count = 0) { assert(msg_length + ecc_length < 256); const uint8_t *src_ptr = (const uint8_t*) src; const uint8_t *ecc_ptr = (const uint8_t*) ecc; uint8_t *dst_ptr = (uint8_t*) dst; const uint8_t src_len = msg_length + ecc_length; const uint8_t dst_len = msg_length; bool ok; /* Allocation memory on stack */ uint8_t stack_memory[MSG_CNT * msg_length + POLY_CNT * ecc_length * 2]; this->memory = stack_memory; Poly *msg_in = &polynoms[ID_MSG_IN]; Poly *msg_out = &polynoms[ID_MSG_OUT]; Poly *epos = &polynoms[ID_ERASURES]; // Copying message to polynomials memory msg_in->Set(src_ptr, msg_length); msg_in->Set(ecc_ptr, ecc_length, msg_length); msg_out->Copy(msg_in); // Copying known errors to polynomial if(erase_pos == NULL) { epos->length = 0; } else { epos->Set(erase_pos, erase_count); for(uint8_t i = 0; i < epos->length; i++){ msg_in->at(epos->at(i)) = 0; } } // Too many errors if(epos->length > ecc_length) return 1; Poly *synd = &polynoms[ID_SYNDROMES]; Poly *eloc = &polynoms[ID_ERRORS_LOC]; Poly *reloc = &polynoms[ID_TPOLY1]; Poly *err = &polynoms[ID_ERRORS]; Poly *forney = &polynoms[ID_FORNEY]; // Calculating syndrome CalcSyndromes(msg_in); // Checking for errors bool has_errors = false; for(uint8_t i = 0; i < synd->length; i++) { if(synd->at(i) != 0) { has_errors = true; break; } } // Going to exit if no errors if(!has_errors) goto return_corrected_msg; CalcForneySyndromes(synd, epos, src_len); FindErrorLocator(forney, NULL, epos->length); // Reversing syndrome // TODO optimize through special Poly flag reloc->length = eloc->length; for(int8_t i = eloc->length-1, j = 0; i >= 0; i--, j++){ reloc->at(j) = eloc->at(i); } // Find errors ok = FindErrors(reloc, src_len); if(!ok) return 1; // Error happened while finding errors (so helpful :D) if(err->length == 0) return 1; /* Adding found errors with known */ for(uint8_t i = 0; i < err->length; i++) { epos->Append(err->at(i)); } // Correcting errors CorrectErrata(synd, epos, msg_in); return_corrected_msg: // Writing corrected message to output buffer msg_out->length = dst_len; memcpy(dst_ptr, msg_out->ptr(), msg_out->length * sizeof(uint8_t)); return 0; } /* @brief Message block decoding * @param *src - encoded message buffer (msg_length + ecc_length size) * @param *msg_out - output buffer (msg_length size at least) * @param *erase_pos - known errors positions * @param erase_count - count of known errors * @return RESULT_SUCCESS if successful, error code otherwise */ int Decode(const void* src, void* dst, uint8_t* erase_pos = NULL, size_t erase_count = 0) { const uint8_t *src_ptr = (const uint8_t*) src; const uint8_t *ecc_ptr = src_ptr + msg_length; return DecodeBlock(src, ecc_ptr, dst, erase_pos, erase_count); } #ifndef RS_DEBUG private: #endif enum POLY_ID { ID_MSG_IN = 0, ID_MSG_OUT, ID_GENERATOR, // 3 ID_TPOLY1, // T for Temporary ID_TPOLY2, ID_MSG_E, // 5 ID_TPOLY3, // 6 ID_TPOLY4, ID_SYNDROMES, ID_FORNEY, ID_ERASURES_LOC, ID_ERRORS_LOC, ID_ERASURES, ID_ERRORS, ID_COEF_POS, ID_ERR_EVAL }; // Pointer for polynomials memory on stack uint8_t* memory; Poly polynoms[MSG_CNT + POLY_CNT]; void GeneratorPoly() { Poly *gen = polynoms + ID_GENERATOR; gen->at(0) = 1; gen->length = 1; Poly *mulp = polynoms + ID_TPOLY1; Poly *temp = polynoms + ID_TPOLY2; mulp->length = 2; for(int8_t i = 0; i < ecc_length; i++){ mulp->at(0) = 1; mulp->at(1) = gf::pow(2, i); gf::poly_mul(gen, mulp, temp); gen->Copy(temp); } } void CalcSyndromes(const Poly *msg) { Poly *synd = &polynoms[ID_SYNDROMES]; synd->length = ecc_length+1; synd->at(0) = 0; for(uint8_t i = 1; i < ecc_length+1; i++){ synd->at(i) = gf::poly_eval(msg, gf::pow(2, i-1)); } } void FindErrataLocator(const Poly *epos) { Poly *errata_loc = &polynoms[ID_ERASURES_LOC]; Poly *mulp = &polynoms[ID_TPOLY1]; Poly *addp = &polynoms[ID_TPOLY2]; Poly *apol = &polynoms[ID_TPOLY3]; Poly *temp = &polynoms[ID_TPOLY4]; errata_loc->length = 1; errata_loc->at(0) = 1; mulp->length = 1; addp->length = 2; for(uint8_t i = 0; i < epos->length; i++){ mulp->at(0) = 1; addp->at(0) = gf::pow(2, epos->at(i)); addp->at(1) = 0; gf::poly_add(mulp, addp, apol); gf::poly_mul(errata_loc, apol, temp); errata_loc->Copy(temp); } } void FindErrorEvaluator(const Poly *synd, const Poly *errata_loc, Poly *dst, uint8_t ecclen) { Poly *mulp = &polynoms[ID_TPOLY1]; gf::poly_mul(synd, errata_loc, mulp); Poly *divisor = &polynoms[ID_TPOLY2]; divisor->length = ecclen+2; divisor->Reset(); divisor->at(0) = 1; gf::poly_div(mulp, divisor, dst); } void CorrectErrata(const Poly *synd, const Poly *err_pos, const Poly *msg_in) { Poly *c_pos = &polynoms[ID_COEF_POS]; Poly *corrected = &polynoms[ID_MSG_OUT]; c_pos->length = err_pos->length; for(uint8_t i = 0; i < err_pos->length; i++) c_pos->at(i) = msg_in->length - 1 - err_pos->at(i); /* uses t_poly 1, 2, 3, 4 */ FindErrataLocator(c_pos); Poly *errata_loc = &polynoms[ID_ERASURES_LOC]; /* reversing syndromes */ Poly *rsynd = &polynoms[ID_TPOLY3]; rsynd->length = synd->length; for(int8_t i = synd->length-1, j = 0; i >= 0; i--, j++) { rsynd->at(j) = synd->at(i); } /* getting reversed error evaluator polynomial */ Poly *re_eval = &polynoms[ID_TPOLY4]; /* uses T_POLY 1, 2 */ FindErrorEvaluator(rsynd, errata_loc, re_eval, errata_loc->length-1); /* reversing it back */ Poly *e_eval = &polynoms[ID_ERR_EVAL]; e_eval->length = re_eval->length; for(int8_t i = re_eval->length-1, j = 0; i >= 0; i--, j++) { e_eval->at(j) = re_eval->at(i); } Poly *X = &polynoms[ID_TPOLY1]; /* this will store errors positions */ X->length = 0; int16_t l; for(uint8_t i = 0; i < c_pos->length; i++){ l = 255 - c_pos->at(i); X->Append(gf::pow(2, -l)); } /* Magnitude polynomial Shit just got real */ Poly *E = &polynoms[ID_MSG_E]; E->Reset(); E->length = msg_in->length; uint8_t Xi_inv; Poly *err_loc_prime_temp = &polynoms[ID_TPOLY2]; uint8_t err_loc_prime; uint8_t y; for(uint8_t i = 0; i < X->length; i++){ Xi_inv = gf::inverse(X->at(i)); err_loc_prime_temp->length = 0; for(uint8_t j = 0; j < X->length; j++){ if(j != i){ err_loc_prime_temp->Append(gf::sub(1, gf::mul(Xi_inv, X->at(j)))); } } err_loc_prime = 1; for(uint8_t j = 0; j < err_loc_prime_temp->length; j++){ err_loc_prime = gf::mul(err_loc_prime, err_loc_prime_temp->at(j)); } y = gf::poly_eval(re_eval, Xi_inv); y = gf::mul(gf::pow(X->at(i), 1), y); E->at(err_pos->at(i)) = gf::div(y, err_loc_prime); } gf::poly_add(msg_in, E, corrected); } bool FindErrorLocator(const Poly *synd, Poly *erase_loc = NULL, size_t erase_count = 0) { Poly *error_loc = &polynoms[ID_ERRORS_LOC]; Poly *err_loc = &polynoms[ID_TPOLY1]; Poly *old_loc = &polynoms[ID_TPOLY2]; Poly *temp = &polynoms[ID_TPOLY3]; Poly *temp2 = &polynoms[ID_TPOLY4]; if(erase_loc != NULL) { err_loc->Copy(erase_loc); old_loc->Copy(erase_loc); } else { err_loc->length = 1; old_loc->length = 1; err_loc->at(0) = 1; old_loc->at(0) = 1; } uint8_t synd_shift = 0; if(synd->length > ecc_length) { synd_shift = synd->length - ecc_length; } uint8_t K = 0; uint8_t delta = 0; uint8_t index; for(uint8_t i = 0; i < ecc_length - erase_count; i++){ if(erase_loc != NULL) K = erase_count + i + synd_shift; else K = i + synd_shift; delta = synd->at(K); for(uint8_t j = 1; j < err_loc->length; j++) { index = err_loc->length - j - 1; delta ^= gf::mul(err_loc->at(index), synd->at(K-j)); } old_loc->Append(0); if(delta != 0) { if(old_loc->length > err_loc->length) { gf::poly_scale(old_loc, temp, delta); gf::poly_scale(err_loc, old_loc, gf::inverse(delta)); err_loc->Copy(temp); } gf::poly_scale(old_loc, temp, delta); gf::poly_add(err_loc, temp, temp2); err_loc->Copy(temp2); } } uint32_t shift = 0; while(err_loc->length && err_loc->at(shift) == 0) shift++; uint32_t errs = err_loc->length - shift - 1; if(((errs - erase_count) * 2 + erase_count) > ecc_length){ return false; /* Error count is greater than we can fix! */ } memcpy(error_loc->ptr(), err_loc->ptr() + shift, (err_loc->length - shift) * sizeof(uint8_t)); error_loc->length = (err_loc->length - shift); return true; } bool FindErrors(const Poly *error_loc, size_t msg_in_size) { Poly *err = &polynoms[ID_ERRORS]; uint8_t errs = error_loc->length - 1; err->length = 0; for(uint8_t i = 0; i < msg_in_size; i++) { if(gf::poly_eval(error_loc, gf::pow(2, i)) == 0) { err->Append(msg_in_size - 1 - i); } } /* Sanity check: * the number of err/errata positions found * should be exactly the same as the length of the errata locator polynomial */ if(err->length != errs) /* couldn't find error locations */ return false; return true; } void CalcForneySyndromes(const Poly *synd, const Poly *erasures_pos, size_t msg_in_size) { Poly *erase_pos_reversed = &polynoms[ID_TPOLY1]; Poly *forney_synd = &polynoms[ID_FORNEY]; erase_pos_reversed->length = 0; for(uint8_t i = 0; i < erasures_pos->length; i++){ erase_pos_reversed->Append(msg_in_size - 1 - erasures_pos->at(i)); } forney_synd->Reset(); forney_synd->Set(synd->ptr()+1, synd->length-1); uint8_t x; for(uint8_t i = 0; i < erasures_pos->length; i++) { x = gf::pow(2, erase_pos_reversed->at(i)); for(int8_t j = 0; j < forney_synd->length - 1; j++){ forney_synd->at(j) = gf::mul(forney_synd->at(j), x) ^ forney_synd->at(j+1); } } } }; } #endif // RS_HPP ================================================ FILE: tests/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 2.8.10) project(RStest C CXX) include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) if(COMPILER_SUPPORTS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") elseif(COMPILER_SUPPORTS_CXX0X) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") else() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") endif() include_directories(${RS_INCLUDE_DIRS}) file(GLOB CPP_FILES *.cpp) add_executable(${PROJECT_NAME} ${CPP_FILES}) ================================================ FILE: tests/gftest.cpp ================================================ /* Author: Mike Lubinets (aka mersinvald) * Date: 29.12.15 * * See LICENSE */ #include "gftest.hpp" Report GFtest::run_tests() { bool (*tests[])(std::string&) = { test_add, test_mul, test_div, test_pow, test_inverse, test_poly_scale, test_poly_add, test_poly_mul, test_poly_div, test_poly_eval }; Report rep = {10, 0}; for(uint i = 0; i < rep.overall; i++) { if(test(tests[i])) rep.passed++; } std::cout << "GFTest: " << rep.passed << "/" << rep.overall << " tests passed.\n\n"; return rep; } bool GFtest::test_add(std::string &name) { INIT_TESTCASE; const uint testcount = 256; uint8_t* leftops = (uint8_t*) RS::gf::log; uint8_t* rightops = (uint8_t*) RS::gf::exp; uint8_t answers[testcount] = { 1, 2, 5, 17, 18, 18, 90, 70, 30, 229, 71, 6, 214, 239, 212, 109, 72, 252, 205, 84, 128, 248, 5, 72, 147, 194, 111, 244, 208, 56, 44, 177, 152, 173, 43, 179, 196, 110, 155, 20, 95, 71, 59, 173, 30, 211, 29, 102, 91, 57, 199, 119, 126, 15, 169, 25, 148, 32, 96, 170, 244, 139, 172, 7, 89, 1, 234, 160, 255, 242, 110, 65, 135, 82, 172, 188, 14, 173, 90, 120, 203, 55, 71, 117, 228, 64, 106, 194, 15, 51, 204, 255, 216, 142, 55, 162, 199, 237, 245, 37, 210, 106, 58, 230, 102, 32, 28, 60, 42, 56, 221, 243, 75, 65, 165, 227, 242, 248, 190, 184, 117, 162, 9, 105, 228, 192, 193, 155, 130, 103, 238, 171, 52, 237, 185, 164, 40, 212, 255, 175, 181, 208, 212, 76, 75, 232, 3, 94, 116, 28, 225, 214, 88, 214, 171, 171, 199, 245, 62, 93, 209, 238, 110, 56, 83, 45, 240, 179, 108, 98, 64, 1, 167, 10, 79, 158, 17, 141, 120, 224, 130, 27, 63, 90, 17, 11, 87, 143, 226, 58, 239, 227, 157, 52, 113, 188, 127, 246, 163, 120, 216, 47, 57, 12, 162, 171, 60, 80, 61, 3, 98, 224, 80, 111, 172, 69, 56, 251, 173, 231, 23, 137, 180, 83, 217, 125, 23, 32, 161, 211, 84, 164, 252, 6, 237, 0, 177, 254, 39, 193, 99, 246, 101, 148, 28, 14, 98, 107, 111, 224, 152, 50, 5, 23, 214, 174 }; for(uint i = 0; i < testcount; i++) { SUBTEST(compare(RS::gf::add(leftops[i], rightops[i]), answers[i])); } RETURN; } // TODO Implement other tests bool GFtest::test_mul(std::string &name) { INIT_TESTCASE; SUBTEST(true); RETURN; } bool GFtest::test_div(std::string &name) { INIT_TESTCASE; SUBTEST(true); RETURN; } bool GFtest::test_pow(std::string &name) { INIT_TESTCASE; SUBTEST(true); RETURN; } bool GFtest::test_inverse(std::string &name) { INIT_TESTCASE; SUBTEST(true); RETURN; } bool GFtest::test_poly_scale(std::string &name) { INIT_TESTCASE; SUBTEST(true); RETURN; } bool GFtest::test_poly_add(std::string &name) { INIT_TESTCASE; SUBTEST(true); RETURN; } bool GFtest::test_poly_mul(std::string &name) { INIT_TESTCASE; SUBTEST(true); RETURN; } bool GFtest::test_poly_div(std::string &name) { INIT_TESTCASE; SUBTEST(true); RETURN; } bool GFtest::test_poly_eval(std::string &name) { INIT_TESTCASE; SUBTEST(true); RETURN; } ================================================ FILE: tests/gftest.hpp ================================================ /* Author: Mike Lubinets (aka mersinvald) * Date: 29.12.15 * * See LICENSE */ #ifndef GFTEST_H #define GFTEST_H #include "testsuite.hpp" #include class GFtest : public TestSuite { public: static Report run_tests(); static bool test_add(std::string& name); static bool test_sub(std::string& name); static bool test_mul(std::string& name); static bool test_div(std::string& name); static bool test_pow(std::string& name); static bool test_inverse(std::string& name); static bool test_poly_scale(std::string& name); static bool test_poly_add (std::string& name); static bool test_poly_mul (std::string& name); static bool test_poly_div (std::string& name); static bool test_poly_eval (std::string& name); }; #endif // GFTEST_H ================================================ FILE: tests/performancetest.cpp ================================================ #include "performancetest.hpp" #include using namespace std::chrono; PerformanceTest::Data_t PerformanceTest::data = {}; RS::ReedSolomon PerformanceTest::rs; PReport PerformanceTest::run_tests(uint32_t laps_cnt, uint32_t cnt_per_lap) { assert(laps_cnt < 64 && cnt_per_lap < 64); std::cout << "Starting performance tests\n"; PReport enc_rep; PReport dec_rep; /// Init data for(uint32_t i = 0; i < TEST_DATA_SIZE; i++) { data.msg[i] = i; data.err_msg[i] = i; } /// Prepare corrupted message for(uint32_t i = 0; i < TEST_ECC_SIZE / 2; i++) { data.err_msg[i+1] = 0; } auto lap_time = []() -> uint32_t { static milliseconds last_time = duration_cast( system_clock::now().time_since_epoch() ); milliseconds time = duration_cast( system_clock::now().time_since_epoch() ); milliseconds diff = time - last_time; last_time = time; return diff.count(); }; /// Run 3 times for(uint32_t i = 0; i < laps_cnt; i++) { lap_time(); /// init static counter for(uint32_t i = 0; i < cnt_per_lap; i++) { encoder_atom(); } enc_rep.laps[i] = lap_time(); lap_time(); /// init static counter for(uint32_t i = 0; i < cnt_per_lap; i++) { decoder_atom(); } dec_rep.laps[i] = lap_time(); } /// Calculate average enc_rep.average[0] = 0; dec_rep.average[0] = 0; for(uint32_t i = 0; i < laps_cnt; i++) { enc_rep.average[0] += enc_rep.laps[i]; dec_rep.average[0] += dec_rep.laps[i]; } enc_rep.average[0] /= laps_cnt; dec_rep.average[0] /= laps_cnt; return PReport { .overall = enc_rep.average[0] + dec_rep.average[0], .average = {enc_rep.average[0], dec_rep.average[0]}, .count = 2 }; } void PerformanceTest::encoder_atom() { rs.EncodeBlock(data.msg, data.ecc); } void PerformanceTest::decoder_atom() { rs.DecodeBlock(data.err_msg, data.ecc, data.rep_msg); } ================================================ FILE: tests/performancetest.hpp ================================================ #ifndef PERFORMANCETEST_HPP #define PERFORMANCETEST_HPP #include "testsuite.hpp" #include "string.h" #include "rs.hpp" #define TEST_DATA_SIZE 200 #define TEST_ECC_SIZE 40 class PerformanceTest : public TestSuite { public: static PReport run_tests(uint32_t laps_cnt, uint32_t cnt_per_lap); static void encoder_atom(); static void decoder_atom(); private: static struct Data_t { uint8_t msg [TEST_DATA_SIZE]; uint8_t err_msg[TEST_DATA_SIZE]; uint8_t rep_msg[TEST_DATA_SIZE]; uint8_t ecc[TEST_ECC_SIZE]; } data; static RS::ReedSolomon rs; }; #endif // PERFORMANCETEST_H ================================================ FILE: tests/rstest.cpp ================================================ /* Author: Mike Lubinets (aka mersinvald) * Date: 29.12.15 * * See LICENSE */ #include "rstest.hpp" #include Report RStest::run_tests() { bool (*tests[])(std::string&) = { test_encode, test_decode, test_stress }; Report rep = {3, 0}; for(uint i = 0; i < rep.overall; i++) { if(test(tests[i])) rep.passed++; } std::cout << "RSTest: " << rep.passed << "/" << rep.overall << " tests passed.\n\n"; return rep; } bool RStest::test_encode(std::string &name) { INIT_TESTCASE; RS::ReedSolomon<30, 8> rs; char message[30]; char encoded[38]; uint8_t right[38] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 99, 26, 219, 193, 9, 94, 186, 143 }; memcpy(message, right, 30); rs.Encode(message, encoded); SUBTEST(compare((uint8_t*)encoded, (uint8_t*)right, 38)); RETURN; } bool RStest::test_decode(std::string &name) { INIT_TESTCASE; RS::ReedSolomon<30, 8> rs; char message[30]; char right[30] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 }; // Test clean message uint8_t clean[38] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 99, 26, 219, 193, 9, 94, 186, 143 }; SUBTEST(rs.Decode(clean, message) == 0); SUBTEST(compare(message, right, 30)); // Test corrupted message uint8_t corrupted[38] = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 14, 0, 16, 17, 18, 19, 0, 21, 22, 23, 24, 25, 26, 27, 28, 29, 99, 26, 219, 193, 9, 94, 0, 143 }; SUBTEST(rs.Decode(corrupted, message) == 0); SUBTEST(compare(message, right, 30)); RETURN } // TODO make complex encoder-decder test bool RStest::test_stress(std::string &name) { INIT_TESTCASE; SUBTEST(true); RETURN; } ================================================ FILE: tests/rstest.hpp ================================================ /* Author: Mike Lubinets (aka mersinvald) * Date: 29.12.15 * * See LICENSE */ #ifndef RSTEST_H #define RSTEST_H #include "testsuite.hpp" #include class RStest : public TestSuite { public: static Report run_tests(); static bool test_encode(std::string& name); static bool test_decode(std::string& name); static bool test_stress(std::string& name); }; #endif // RSTEST_H ================================================ FILE: tests/tests.cpp ================================================ /* Author: Mike Lubinets (aka mersinvald) * Date: 29.12.15 * * See LICENSE */ #include #include "gftest.hpp" #include "rstest.hpp" #include "performancetest.hpp" using namespace std; #define COUNT_PER_PTEST_LAP 63 #define COUNT_OF_LAPS 22 int main() { GFtest::run_tests(); RStest::run_tests(); PReport performance = PerformanceTest::run_tests(COUNT_OF_LAPS, COUNT_PER_PTEST_LAP); std::cout << "Average of " << COUNT_OF_LAPS << "x" << COUNT_PER_PTEST_LAP << " for encoder: " << performance.average[0] << "ms\n"; std::cout << "Average of " << COUNT_OF_LAPS << "x" << COUNT_PER_PTEST_LAP << " for decoder: " << performance.average[1] << "ms\n"; } ================================================ FILE: x32.cmake_toolchain ================================================ set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_CXX_COMPILER_ARG1 "-m32") set(CMAKE_C_COMPILER_ARG1 "-m32")