/* * Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library * * Copyright (c) 2017, Hard Consulting Corporation. * * Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. See the LICENSE file at the top of the * source tree. Ezpwd Reed-Solomon is also available under Commercial license. The Djelic BCH code * under djelic/ and the c++/ezpwd/bch_base wrapper is redistributed under the terms of the GPLv2+, * regardless of the overall licensing terms. * * Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. */ #ifndef _EZPWD_BCH #define _EZPWD_BCH #include #include "rs_base" // Basic DEBUG, EZPWD_... preprocessor stuff, ezpwd::log_, etc. #include "bch_base" namespace ezpwd { // // ezpwd::bch_base -- Interface to underlying Djelic Linux Kernel API // ezpwd::bch -- General BCH codec types; min. CORRECTION capacity, undefined PAYLOAD // // These implementations retain the original Djelic Linux Kernel API; specifically, they find // a BCH codec of the given Galois order M (ie. has codewords of size 2**M-1), and at least the // target bit-error correction capacity T. They may correct more than T errors, and the number // of parity ECC bits will be selected by the algorithm. You need to compute the maximum // non-parity payload by computing _bch->n - _bch->ecc_bits. // // The data and parity bits must always be on separate blocks of int8_t/uint8_t-sized data // in the container. This is required because the underlying API must break the data and parity // out as separate arrays for processing. So, if the computed ecc_bits is not evenly divisible // by 8, some care must be taken to ensure that it is packed into exactly ecc_bytes of data at // the end of the supplied container. Alternatively, it can be kept in a separate container. // This is probably safest, as the bch_base/bch/BCH classes will never attempt to resize the // data/parity containers when supplied separately. // // Like the Reed-Solomon APIs, the bch_base/bch/BCH APIs will alter the size of a variable // container in encode(...), to add the BCH ECC "parity" data (eg. std::vector, std::string). // Fixed containers (eg. std::array) are never resized, and it is assumed that ecc_bytes of // parity data exist at the end of the container. // class bch_base { public: ezpwd::bch_control *_bch; bch_base( const bch_base & ) = delete; // no copy-constructor bch_base( size_t m, size_t t, unsigned int prim_poly = 0 ) : _bch( ezpwd::init_bch( int( m ), int( t ), prim_poly )) { ; } virtual ~bch_base() { ezpwd::free_bch( this->_bch ); } size_t ecc_bytes() const { return _bch->ecc_bytes; } size_t ecc_bits() const { return _bch->ecc_bits; } size_t t() const { return _bch->t; } // // << bch_base -- output codec in standard BCH( N, N-ECC, T ) form // virtual std::ostream &output( std::ostream &lhs ) const { return lhs << *this->_bch; } // // encode -- container interfaces // // Returns number of ECC *bits* initialized (to be consistent w/ decode, which returns // number of bit errors corrected). // int encode( std::string &data ) const { typedef uint8_t uT; typedef std::pair uTpair; data.resize( data.size() + ecc_bytes() ); return encode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() )); } int encode( const std::string &data, std::string &parity ) const { typedef uint8_t uT; typedef std::pair cuTpair; typedef std::pair uTpair; parity.resize( ecc_bytes() ); return encode( cuTpair( (const uT *)&data.front(), (const uT *)&data.front() + data.size() ), uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() )); } template < typename T > int encode( std::vector &data ) const { typedef typename std::make_unsigned::type uT; typedef std::pair uTpair; data.resize( data.size() + ecc_bytes() ); return encode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() )); } template < typename T > int encode( const std::vector&data, std::vector &parity ) const { typedef typename std::make_unsigned::type uT; typedef std::pair cuTpair; typedef std::pair uTpair; parity.resize( ecc_bytes() ); return encode( cuTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ), uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() )); } template < typename T, size_t N > int encode( std::array &data, int pad = 0 ) // ignore 'pad' symbols at start of array const { typedef typename std::make_unsigned::type uT; typedef std::pair uTpair; return encode( uTpair( (uT *)&data.front() + pad, (uT *)&data.front() + data.size() )); } // // encode -- denote data+parity or data, parity using pairs of uint8_t iterators // encode -- base implementation, in terms of uint8_t pointers // virtual int encode( const std::pair &data ) const { return encode( data.first, data.second - data.first - ecc_bytes(), data.second - ecc_bytes() ); } virtual int encode( const std::pair &data, const std::pair &parity ) const { if ( size_t( parity.second - parity.first ) != ecc_bytes() ) { EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: parity length incompatible with number of ECC bytes", -1 ); } return encode( data.first, data.second - data.first, parity.first ); } virtual int encode( const uint8_t *data, size_t len, uint8_t *parity ) const { memset( parity, 0, ecc_bytes() ); // Djelic encode_bch requires ECC to be initialized to 0 ezpwd::encode_bch( this->_bch, data, len, parity ); return int( ecc_bits() ); } // // decode -- container interface, w/ optional corrected bit-error positions reported // // Does not correct errors in parity! // int decode( std::string &data, std::vector *position= 0 ) const { typedef uint8_t uT; typedef std::pair uTpair; return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ), position ); } int decode( std::string &data, std::string &parity, std::vector *position= 0 ) const { typedef uint8_t uT; typedef std::pair uTpair; return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ), uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() ), position ); } template < typename T > int decode( std::vector &data, std::vector *position= 0 ) const { typedef typename std::make_unsigned::type uT; typedef std::pair uTpair; return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ), position ); } template < typename T > int decode( std::vector &data, std::vector &parity, std::vector *position= 0 ) const { typedef typename std::make_unsigned::type uT; typedef std::pair uTpair; return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ), uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() ), position ); } template < typename T, size_t N > int decode( std::array &data, int pad = 0, // ignore 'pad' symbols at start of array std::vector *position= 0 ) const { typedef typename std::make_unsigned::type uT; typedef std::pair uTpair; return decode( uTpair( (uT *)&data.front() + pad, (uT *)&data.front() + data.size() ), position ); } // // decode -- denote data+parity or data, parity using pairs of uint8_t iterators // decode -- decode and correct BCH codeword, returning number of corrections, or -1 if failed // // Corrects data in-place (unlike the basic Djelic Linux Kernel API, which only returns // error positions. For consistency with ezpwd::rs..., we report all error positions as // std::vector, even though the underlying Djelic API reports them as arrays of // unsigned int. // virtual int decode( const std::pair &data, std::vector *position= 0 ) const { return decode( data.first, data.second - data.first - ecc_bytes(), data.second - ecc_bytes(), position ); } virtual int decode( const std::pair &data, const std::pair &parity, std::vector *position= 0 ) const { if ( size_t( parity.second - parity.first ) != ecc_bytes() ) { EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: parity length incompatible with number ECC bytes", -1 ); } return decode( data.first, data.second - data.first, parity.first, position ); } virtual int decode( uint8_t *data, size_t len, uint8_t *parity, std::vector *position= 0 ) const { if ( position ) position->resize( t() * 2 ); // may be able to correct beyond stated capacity! int corrects = ezpwd::correct_bch( this->_bch, data, len, parity, 0, 0, position ? (unsigned int *)&(*position)[0] : 0 ); if ( position && corrects >= 0 ) position->resize( corrects ); return corrects; } // // {en,de}coded -- returns an encoded/corrected copy of the provided container // // NOTE: // // Must return exceptions on failure; If exceptions inhibited, returns a // default-constructed instance of the supplied data container. This may be sufficient to // reliably deduce failure; if not, this interface should not be used. // // Overloads decoded to also allow recovery of corrected error positions and count. // template C encoded( C data ) const { if ( encode( data ) < 0 ) EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: Could not encode data", C() ); return data; } template C decoded( C data ) const { if ( decode( data ) < 0 ) EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: Could not decode data", C() ); return data; } template C decoded( C data, std::vector &position ) const { if ( decode( data, &position ) < 0 ) EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: Could not decode data", C() ); return data; } }; // class bch_base template < size_t SYMBOLS, size_t CORRECTION > class bch : public bch_base { public: bch() : bch_base( ezpwd::log_::value, CORRECTION ) { ; } virtual ~bch() { ; } }; // class bch // // std::ostream << ezpwd::bch_base // // Output a BCH codec description in standard form eg. BCH( 255, 239, 2 ) // inline std::ostream &operator<<( std::ostream &lhs, const ezpwd::bch_base &rhs ) { return rhs.output( lhs ); } // // ezpwd::BCH -- Standard BCH codec types // // Specify and create a standard BCH codec with exactly the specified capacities. We create // the undering BCH codec using SYMBOLS and CORRECTION capacity; the actual correction capacity // T, the number of PARITY bits and hence PAYLOAD (CAPACITY - PARITY) is selected automatically // by the underlying Djelic Linux Kernel BCH codec API. For this interface, we demand that the // caller *knows* all of these values at compile time, however, mostly for future optimization // purposes. We validate them, and fail the constructor if they don't match. See bch_test for // an enumeration of all possible BCH codecs. // // In the future, this API may be re-implemented to not use the generic BCH API, but a more // optimized locally-defined implementation that leverages the fixed SYMBOLS, PAYLOAD and // CORRECTION capacities to produce more optimal code. // template < size_t SYMBOLS, size_t PAYLOAD, size_t CORRECTION > class BCH : public bch { public: static const size_t M = ezpwd::log_::value; // Galois field order; eg. 255 --> 8 static const size_t N = SYMBOLS; static const size_t T = CORRECTION; static const size_t LOAD = PAYLOAD; BCH() : bch() { if ( this->_bch->t != T || this->_bch->n != N || this->_bch->n - this->_bch->ecc_bits != LOAD ) { std::ostringstream err; this->output( err ) << " specified doesn't match underlying " << *this->_bch << " produced."; EZPWD_RAISE_OR_ABORT( std::runtime_error, err.str().c_str() ); } } virtual ~BCH() { ; } // // << BCH<...> -- output codec in standard BCH( N, N-ECC, T ) form // virtual std::ostream &output( std::ostream &lhs ) const { return lhs << "BCH( " << std::setw( 3 ) << N << ", " << std::setw( 3 ) << LOAD << ", " << std::setw( 3 ) << T << " )"; } }; // class BCH // // std::ostream << ezpwd::BCH<...> // // Output a BCH codec description in standard form eg. BCH( 255, 239, 2 ) // // NOTE: clang/gcc disagree on the scoping of operator<< template/non-template functions... // template inline std::ostream &operator<<( std::ostream &lhs, const ezpwd::BCH &rhs ) { return rhs.output( lhs ); } } // namespace ezpwd #endif // _EZPWD_BCH