op25/op25/gr-op25_repeater/lib/ezpwd/serialize

1189 lines
40 KiB
Plaintext

/*
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
*
* Copyright (c) 2014, 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. c++/ezpwd/rs_base
* is redistributed under the terms of the LGPL, 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_SERIALIZE
#define _EZPWD_SERIALIZE
#include <iterator>
#include <array>
#include "output"
//
// EZPWD (no padding) and RFC4648 (enforce padding) base-N codecs
//
// scatter -- 8-bit binary data into 5-bit (base32) and 6-bit (base64) chunks (optionally w/pad)
// gather -- collect up 5/6-bit binary data back into 8-bit characters (optionally w/pad)
// ..._standard -- enforce RFC4648-standard padding
//
// Scatters or gathers 8-bit binary character data to 5/6-bit symbols, suitable for base32/64
// encoding.
//
// encode -- convert binary data to ASCII base32/64, in-place
// decode -- convert ASCII base32/64 to binary data, in-place
// ..._standard -- enforce RFC4648-standard padding
//
// Transforms iterable containers of char between ASCII symbols and binary data, always in-place.
// The decode may alter the size of the result (by ignoring whitespace).
//
// In general the ezpwd::base32/64 en/decoders are designed to produce easily human-usable
// encodings, and can ignore common whitespace characters and '-' to allow human-readable
// formatting. The RFC4648 Standard base 32/64 and Crockford base32 encodings are also supported.
//
// Adding new symbol encodings (and even new bases, up to base-128) is trivial.
//
namespace ezpwd {
namespace serialize {
enum chr_t {
pd = -1, // padding
nv = -2, // invalid
ws = -3, // whitespace
};
enum ws_use_t {
ws_invalid = 0, // Whitespace is invalid
ws_ignored = 1, // Whitespace ignored (the default)
};
enum pd_use_t {
pd_invalid = 0, // Padding is not expected (invalid)
pd_ignored = 1, // Padding is ignored, and automatically supplied if required (the default)
pd_enforce = 2, // Padding is expected and enforced
};
//
// serialize::tables -- base class for TABLES in all base<N,TABLES>
//
// Every serialize::table<BASE> specialization must have an encoder table of size BASE,
// and a decoder table of size 127.
//
//
// hex<16/32> -- RFC4648 4-bit (standard 16 symbol) and 5-bit (32 symbol)
// standard<16/32/64> -- RFC4648 standard tables
// standard_url<64> -- RFC4648 standard tables (base-64 URL-safe)
// ezpwd<16/32/64> -- EZPWD tables
// crockford<32> -- Crockford (base32 only)
//
// These types are passed as the TABLE template parameter to base<32/64,TABLE> class
// template instantiations. They must specify an encoder table of size BASE, and a decoder
// table of size 127.
//
template < size_t N > struct hex { };
template < size_t N > struct uuencode { };
template < size_t N > struct standard { };
template < size_t N > struct standard_url { };
template < size_t N > struct ezpwd { };
template < size_t N > struct crockford { };
//
// base<16> tables -- basic hexadecimal supported (hex, standard, ezpwd identical)
//
template <> struct hex<16> {
static const constexpr std::array<char,16>
encoder { {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
} };
static const constexpr std::array<char,127>
decoder { {
nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, ws, ws, ws, ws, nv, nv, // 9-13: <TAB>,<NL>,<VT>,<FF>,<CR>
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, //
ws, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, // !"#$%&`()*+,-./
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, nv, nv, nv, nv, nv, nv, // 0123456789:;<=>? '=' is pad
nv, 10, 11, 12, 13, 14, 15, nv, nv, nv, nv, nv, nv, nv, nv, nv, // @ABCDEFGHIJKLMNO
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, // PQRSTUVWXYZ[\]^_
nv, 10, 11, 12, 13, 14, 15, nv, nv, nv, nv, nv, nv, nv, nv, nv, // `abcdefghijklmno
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, // pqrstuvwxyz{|}~
} };
}; // struct serialize::hex<16>
template <> struct standard<16> {
static const constexpr std::array<char,16>
encoder = hex<16>::encoder;
static const constexpr std::array<char,127>
decoder = hex<16>::decoder;
};
template <> struct ezpwd<16> {
static const constexpr std::array<char,16>
encoder = hex<16>::encoder;
static const constexpr std::array<char,127>
decoder = hex<16>::decoder;
};
//
// base<32> tables -- ezpwd, and RFC4648 hex32, crockford and standard
//
template <> struct hex<32> {
static const constexpr std::array<char,32>
encoder { {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
} };
static const constexpr std::array<char,127>
decoder { {
nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, ws, ws, ws, ws, nv, nv, // 9-13: <TAB>,<NL>,<VT>,<FF>,<CR>
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, //
ws, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, // !"#$%&`()*+,-./
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, nv, nv, nv, nv, nv, nv, // 0123456789:;<=>? '=' is pad
nv, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // @ABCDEFGHIJKLMNO
25, 26, 27, 28, 29, 30, 31, nv, nv, nv, nv, nv, nv, nv, nv, nv, // PQRSTUVWXYZ[\]^_
nv, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // `abcdefghijklmno
25, 26, 27, 28, 29, 30, 31, nv, nv, nv, nv, nv, nv, nv, nv, // pqrstuvwxyz{|}~
} };
}; // struct serialize::hex<32>
template <> struct standard<32> { // RFC4648 Standard
static const constexpr std::array<char,32>
encoder { {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
} };
static const constexpr std::array<char,127>
decoder { {
nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, ws, ws, ws, ws, nv, nv, // 9-13: <TAB>,<NL>,<VT>,<FF>,<CR>
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, //
ws, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, // !"#$%&`()*+,-./
nv, nv, 26, 27, 28, 29, 30, 31, nv, nv, nv, nv, nv, pd, nv, nv, // 0123456789:;<=>?
nv, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // @ABCDEFGHIJKLMNO '=' is pad
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, nv, nv, nv, nv, nv, // PQRSTUVWXYZ[\]^_
nv, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // `abcdefghijklmno
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, nv, nv, nv, nv, // pqrstuvwxyz{|}~
} };
}; // struct serialize::standard<32>
template <> struct ezpwd<32> {
static const constexpr std::array<char,32>
encoder { {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P',
'Q', 'R', 'T', 'U', 'V', 'W', 'X', 'Y',
} };
static const constexpr std::array<char,127>
decoder { {
nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, ws, ws, ws, ws, nv, nv, // 9-13: <TAB>,<NL>,<VT>,<FF>,<CR>
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, //
ws, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, nv, nv, // !"#$%&`()*+,-./ '-' is whitespace
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, nv, nv, nv, pd, nv, nv, // 0123456789:;<=>? '=' is pad
nv, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 20, 21, 22, 0, // @ABCDEFGHIJKLMNO
23, 24, 25, 5, 26, 27, 28, 29, 30, 31, 2, nv, nv, nv, nv, nv, // PQRSTUVWXYZ[\]^_
nv, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 20, 21, 22, 0, // `abcdefghijklmno
23, 24, 25, 5, 26, 27, 28, 29, 30, 31, 2, nv, nv, nv, nv, // pqrstuvwxyz{|}~
} };
}; // struct serialize::ezpwd<32>
template <> struct crockford<32> {
static const constexpr std::array<char,32>
encoder { {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',
'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z',
} };
static const constexpr std::array<char,127>
decoder { {
nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, ws, ws, ws, ws, nv, nv, // 9-13: <TAB>,<NL>,<VT>,<FF>,<CR>
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, //
ws, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, nv, nv, // !"#$%&`()*+,-./ '-' is whitespace
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, nv, nv, nv, pd, nv, nv, // 0123456789:;<=>? '=' is pad
nv, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, 0, // @ABCDEFGHIJKLMNO
22, 23, 24, 25, 26, nv, 27, 28, 29, 30, 31, nv, nv, nv, nv, nv, // PQRSTUVWXYZ[\]^_
nv, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, 0, // `abcdefghijklmno
22, 23, 24, 25, 26, nv, 27, 28, 29, 30, 31, nv, nv, nv, nv, // pqrstuvwxyz{|}~
} };
}; // struct serialize::crockford<32>
//
// base<64> tables
//
template <> struct uuencode<64> {
static const constexpr std::array<char,64>
encoder { {
' ', '!', '"', '#', '$', '%', '&','\'',
'(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '[','\\', ']', '^', '_',
} };
static const constexpr std::array<char,127>
decoder { {
nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, ws, ws, ws, ws, nv, nv, // 9-13: <TAB>,<NL>,<VT>,<FF>,<CR>
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, //
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, 30, 31, // 0123456789:;<=>? '=' is pad
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // @ABCDEFGHIJKLMNO
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // PQRSTUVWXYZ[\]^_
nv, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // `abcdefghijklmno
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, nv, nv, nv, nv, nv, // pqrstuvwxyz{|}~
} };
}; // struct serialize::standard<64>
template <> struct standard<64> {
static const constexpr std::array<char,64> // RFC4648 Standard
encoder { {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/',
} };
static const constexpr std::array<char,127>
decoder { {
nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, ws, ws, ws, ws, nv, nv, // 9-13: <TAB>,<NL>,<VT>,<FF>,<CR>
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, //
ws, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, 62, nv, nv, nv, 63, // !"#$%&`()*+,-./
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, nv, nv, nv, pd, nv, nv, // 0123456789:;<=>? '=' is pad
nv, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // @ABCDEFGHIJKLMNO
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, nv, nv, nv, nv, nv, // PQRSTUVWXYZ[\]^_
nv, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // `abcdefghijklmno
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nv, nv, nv, nv, // pqrstuvwxyz{|}~
} };
}; // struct serialize::standard<64>
template <> struct standard_url<64> {
static const constexpr std::array<char,64>
encoder { {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '-', '_',
} };
static const constexpr std::array<char,127>
decoder { {
nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, ws, ws, ws, ws, nv, nv, // 9-13: <TAB>,<NL>,<VT>,<FF>,<CR>
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, //
ws, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, 62, nv, nv, // !"#$%&`()*+,-./
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, nv, nv, nv, pd, nv, nv, // 0123456789:;<=>? '=' is pad
nv, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // @ABCDEFGHIJKLMNO
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, nv, nv, nv, nv, 63, // PQRSTUVWXYZ[\]^_
nv, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // `abcdefghijklmno
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nv, nv, nv, nv, // pqrstuvwxyz{|}~
} };
}; // struct serialize::standar_url<64>
template <> struct ezpwd<64> {
static const constexpr std::array<char,64> // '+' and '.' are URL safe, and we treat '-' as whitespace
encoder { {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '.',
} };
static const constexpr std::array<char,127>
decoder { {
nv, nv, nv, nv, nv, nv, nv, nv, nv, ws, ws, ws, ws, ws, nv, nv, // 9-13: <TAB>,<NL>,<VT>,<FF>,<CR>
nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, //
ws, nv, nv, nv, nv, nv, nv, nv, nv, nv, nv, 62, nv, ws, 63, nv, // !"#$%&`()*+,-./ '-' is whitespace
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, nv, nv, nv, pd, nv, nv, // 0123456789:;<=>? '=' is pad
nv, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // @ABCDEFGHIJKLMNO
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, nv, nv, nv, nv, 63, // PQRSTUVWXYZ[\]^_
nv, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // `abcdefghijklmno
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nv, nv, nv, nv, // pqrstuvwxyz{|}~
} };
}; // struct serialize::ezpwd<64>
//
// base_generic<N, TABLES> -- generic base-N conversion using TABLES<N>. Doesn't have scatter/gather.
//
// Every base-N converter requires a TABLE class of matching N, and can encode/decode.
// in-place using the table::encoder/decoder arrays. Only specific values of N have class
// template specialization which includes scatter/gather methods -- the generic base-N
// template does not..
//
template < size_t N, typename TABLES = ezpwd< N >>
class base_generic
: public TABLES {
public:
static
std::ostream &output(
std::ostream &lhs )
{
lhs << "base<" << N;
#if defined( DEBUG ) && DEBUG >= 3
lhs << "," << TABLES::encoder;
#endif
lhs << ">";
return lhs;
}
//
// base<N>::encode(<string>) -- encode the supplied sequence of data in the domain (0,N] to base-N
// base<N>::encode(<iter>,<iter>) -- encode the supplied std::string of (0,N] symbols in-place to base-N
//
// Items from iterator must be convertible to a table index (ie. not -'ve, and within
// the table size N). If the table contains 'ws' entries, they may optionally be ignored.
// If the input symbol indexes outside the provided encoder table (or an 'nv' entry), then an
// exception will be thrown.
//
// If a non-zero 'pad' character is supplied, then the -1 (pd) character value will be
// allowed (normally occurring only at the end of the input range), and a 'pad' will be
// emitted for each one.
//
template < typename I >
static
I encode(
I begin,
I end,
char pad = '=' ) // '=' for standards-compliance
{
for ( I i = begin; i != end; ++i ) {
if ( *i >= 0 and size_t( *i ) < TABLES::encoder.size() )
*i = TABLES::encoder[*i];
else if ( pd == *i and pad )
*i = pad;
else
throw std::runtime_error(
std::string( "ezpwd::serialize::encode: invalid base-" ) << N
<< " binary symbol presented: " << *i );
}
return end;
}
static
std::string &encode(
std::string &symbols,
char pad = '=' ) // '=' for standards-compliance
{
encode( symbols.begin(), symbols.end(), pad );
return symbols;
}
//
// base<N>::decode(<iter>,<iter>) -- decode base-N symbols in-place, collapsing spaces.
// base<N>::decode(<string>) -- decode base-N symbols in supplied std::string, collapsing spaces, in-place.
//
// Items from iterator must be convertible to a table index (ie. not -'ve, and within
// the table size, which is always 127).
//
// ws_ignored -- skip whitepace/- symbols (the default)
// ws_invalid -- consider whitespace/- symbols as invalid symbols
//
// pd_invalid -- consider padding symbols as invalid
// pd_ignored -- skip padding symbols
// pd_enforce -- leave any padding symbols in place (the default)
//
// If erasure vector supplied, marks invalid symbols as erasures; otherwise, throws
// exception. Ignores whitespace. Will return an iterator to just after the last output
// symbol used in the provided range (eg. to shorten the container, if desired), leaving any
// remaining symbols unchanged. The <string> version returns the same string reference
// passed in (shortened, if spaces/padding ignored).
//
// If an invalid vector is supplied, we'll also return the offending input symbol(s); if
// an exception is raised (no erasure vector supplied), only one symbol will be in invalid.
//
// NOTE: will quite likely return an iterator before the supplied 'end', indicating that the
// output has been truncated (shortened), due to collapsing spaces!
//
template < typename I >
static
I decode(
I begin,
I end,
std::vector<int> *erasure, // Deem invalid symbols as erasures
std::vector<char> *invalid = 0, // and return the symbols
ws_use_t ws_use = ws_ignored, // skip any whitespace
pd_use_t pd_use = pd_invalid ) // no padding expected; invalid
{
if ( erasure )
erasure->clear();
if ( invalid )
invalid->clear();
I i, o;
for ( i = o = begin; i != end; ++i ) {
size_t ti( *i );
char c = ti < TABLES::decoder.size() ? TABLES::decoder[ti] : char( nv );
if ( ws == c )
switch ( ws_use ) {
case ws_invalid:
c = nv;
break;
case ws_ignored: default:
continue;
}
if ( pd == c )
switch ( pd_use ) {
case pd_invalid:
c = nv;
break;
case pd_enforce:
break;
case pd_ignored: default:
continue;
}
if ( nv == c ) {
// Invalid symbol; optionally remember them. Mark as erasure? Or throw.
if ( invalid )
invalid->push_back( *i );
if ( erasure ) {
erasure->push_back( o - begin ); // index of offending symbol in output
c = 0; // will be output w/ 0 value
} else {
throw std::runtime_error(
std::string( "ezpwd::serialize::decode: invalid base-" ) << N
<< " ASCII symbol presented: " << int( *i ) << " '" << *i << "'" );
}
}
*o++ = c;
}
return o;
}
template < typename I >
static
I decode(
I begin,
I end,
ws_use_t ws_use = ws_ignored,
pd_use_t pd_use = pd_invalid )
{
return decode( begin, end, 0, 0, ws_use, pd_use );
}
template < typename I >
static
I decode_standard(
I begin,
I end )
{
return decode( begin, end, 0, 0, ws_ignored, pd_enforce ); // RFC4648 padding
}
static
std::string &decode(
std::string &symbols,
std::vector<int> *erasure = 0,
std::vector<char> *invalid = 0,
ws_use_t ws_use = ws_ignored,
pd_use_t pd_use = pd_invalid )
{
auto last = decode( symbols.begin(), symbols.end(), erasure, invalid,
ws_use, pd_use );
if ( last != symbols.end() )
symbols.resize( last - symbols.begin() ); // eliminated some whitespace
return symbols;
}
static
std::string &decode_standard(
std::string &symbols,
std::vector<int> *erasure = 0,
std::vector<char> *invalid = 0 )
{
return decode( symbols, erasure, invalid, ws_ignored, pd_enforce ); // RFC4648 padding
}
//
// gather_next -- return next symbol to gather into 8-bit output
//
// Fails if padding mixed in with data, or a data symbol exceeds encoding bit depth
// (eg. 5 bits for base-32).
//
// If auto-padding, allow the caller to differentiate
//
template < typename I >
static
int gather_next( I &beg, I end, pd_use_t pd_use, int previous )
{
if ( beg == end ) {
if ( pd_enforce == pd_use )
throw std::logic_error( std::string( "base-" ) << N << " gather error; insufficient data");
// automatically pad; return nv on underflow while un-emitted data remains, finally pd
return previous >= 0 ? nv : pd;
}
int c = *beg++;
if ( previous < 0 and c >= 0 )
throw std::logic_error(
std::string( "base-" ) << N << " gather error; data following padding" );
if ( c >= char( N ) or c < pd )
throw std::logic_error(
std::string( "base-" ) << N << " gather error; symbol value " << int( c ) << " beyond capacity" );
return c;
}
}; // class serialize::base_generic
} // namespace serialize
template < size_t N, typename TABLES >
std::ostream &operator<<(
std::ostream &lhs,
const ezpwd::serialize::base_generic<N,TABLES>
&rhs )
{
return rhs.output( lhs );
}
namespace serialize {
//
// ezpwd::serialize::base<N> -- Arbitrary bases (other than those with specializations below)
//
// Can en/decode, but no scatter/gather implementation, nor encode_size.
//
template < size_t N, typename TABLES >
class base
: public base_generic< N, TABLES > {
public:
using base_generic< N, TABLES >::encode;
using base_generic< N, TABLES >::decode;
};
//
// ezpwd::serialize::base<16> -- transform individual characters between 4-bit binary and a base-16
//
template < typename TABLES >
class base< 16, TABLES >
: public base_generic< 16, TABLES > {
public:
using base_generic< 16, TABLES >::encode;
using base_generic< 16, TABLES >::decode;
using base_generic< 16, TABLES >::gather_next;
static constexpr size_t encode_size(
size_t len,
pd_use_t /* pd_use */ = pd_invalid )
{
return len * 2; // encoding base-16 always exactly doubles size
}
//
// base<16>::scatter -- distribute a range of input bytes to an output iterator in 4-bit chunks
//
// The same implementation is used for both random-access iterators, and for all
// other forward iterators.
//
template < typename I, typename O, typename iterator_tag >
static
O scatter(
I beg,
I end,
O out,
pd_use_t, // ignored; never needs to pad
iterator_tag ) // ignored
{
while ( beg != end ) {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 4);
*out++ = char((c0 & 0x0f));
}
return out;
}
template < typename I, typename O >
static
O scatter(
I beg,
I end,
O out,
pd_use_t pd_use = pd_invalid )
{
return scatter( beg, end, out, pd_use, typename std::iterator_traits<I>::iterator_category() );
}
template < typename I, typename O >
static
O scatter_standard(
I beg,
I end,
O out )
{
return scatter( beg, end, out, pd_enforce, typename std::iterator_traits<I>::iterator_category() );
}
//
// base<16>::gather -- collect 4-bit chunks into 8-bit characters
//
// If underflow occurs (not enough data collected to output complete last char), then an
// exception will be raised. However, if 'pad' is set, then output will automatically be
// padded, flushing out any un-emitted data remaining in previous 4-bit base-16 symbol.
//
// For correct base-16 data produced by 'scatter', this will allow 'gather' to always
// produce the identical output as was originally provided to 'scatter'. However, if simply
// truncated base-16 input is provided (eg. only 1 symbols of an 2-symbol 8-bit
// base-16 group), then the final 8-bit symbol from the original data will be missing.
//
template < typename I, typename O >
static
O gather(
I beg,
I end,
O out,
pd_use_t pd_use = pd_invalid )
{
while ( beg != end ) {
int c0 = gather_next( beg, end, pd_use, 0 );
int c1 = gather_next( beg, end, pd_use, c0 );
if ( c0 >= 0 and c1 >= 0 )
*out++ = ( ((c0 < 0 ? 0 : c0 ) << 4)
| (c1 < 0 ? 0 : c1 ));
}
return out;
}
template < typename I, typename O >
static
O gather_standard(
I beg,
I end,
O out )
{
return gather( beg, end, out, pd_enforce );
}
}; // ezpwd::base<16>
//
// ezpwd::serialize::base16...
//
// Shortcut typedefs for the available base16 codec.
//
typedef base< 16, hex< 16 >>
base16;
typedef base< 16, hex< 16 >>
base16_standard;
//
// ezpwd::serialize::base<32> -- transform individual characters between 5-bit binary and a base-32
//
// The char values [0,32) are mapped by base<32>::encode onto something like
//
// 0123456789ABCDEFGHJKLMNPQRTUVWXY TABLES=ezpwd<32> (default)
// 0123456789ABCDEFGHJKMNPQRSTVWXYZ TABLES=crockford<32>
// ABCDEFGHIJKLMNOPQRSTUVWXYZ234567 TABLES=standard<32>
//
// and base<32>::decode performs the inverse. In addition to folding lower-case to
// upper-case, the following mappings occur in the ezpwd<32>::decoder table:
//
// O -> 0
// Z -> 2
// S -> 5
// I -> 1
//
// B -> 8 could not be included due to the limited size of the capitalized alpha-numeric
// symbol pool, unfortunately, but the other substitutions were prioritized as they are
// more likely to occur in standard printed text.
//
// Any characters encountered outside [0,32) by encode and outside the above set by
// decode raise an exception, unless an erasure vector is provided, in which case we supply
// a 0 value and store the index of the invalid symbol in the vector.
//
template < typename TABLES >
class base< 32, TABLES >
: public base_generic< 32, TABLES > {
public:
using base_generic< 32, TABLES >::encode;
using base_generic< 32, TABLES >::decode;
using base_generic< 32, TABLES >::gather_next;
static constexpr size_t encode_size(
size_t len,
pd_use_t pd_use = pd_invalid )
{
return ( pd_enforce == pd_use
? ( len + 4 ) / 5 * 8
: ( len * 8 + 4 ) / 5 );
}
//
// base<32>::scatter -- distribute a range of input bytes to an output iterator in 5-bit chunks
//
// Separate implementation are provided for random-access iterators (with fewer
// comparisons necessary) and for all other forward iterators.
//
template < typename I, typename O, typename iterator_tag >
static
O scatter(
I beg,
I end,
O out,
pd_use_t pd_use,
iterator_tag )
{
while ( beg != end ) {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 3);
if ( beg == end ) {
*out++ = char((c0 & 0x07) << 2);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
}
return out;
}
int c1 = *beg++;
*out++ = char((c0 & 0x07) << 2 | (c1 & 0xff) >> 6);
*out++ = char((c1 & 0x3f) >> 1);
if ( beg == end ) {
*out++ = char((c1 & 0x01) << 4);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
}
return out;
}
int c2 = *beg++;
*out++ = char((c1 & 0x01) << 4 | (c2 & 0xff) >> 4);
if ( beg == end ) {
*out++ = char((c2 & 0x0f) << 1);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
}
return out;
}
int c3 = *beg++;
*out++ = char((c2 & 0x0f) << 1 | (c3 & 0xff) >> 7);
*out++ = char((c3 & 0x7f) >> 2);
if ( beg == end ) {
*out++ = char((c3 & 0x03) << 3);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
}
return out;
}
int c4 = *beg++;
*out++ = char((c3 & 0x03) << 3 | (c4 & 0xff) >> 5);
*out++ = char((c4 & 0x1f));
}
return out;
}
template < typename I, typename O >
static
O scatter(
I beg,
I end,
O out,
pd_use_t pd_use,
std::random_access_iterator_tag )
{
while ( end - beg >= 5 ) {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 3);
int c1 = *beg++;
*out++ = char((c0 & 0x07) << 2 | (c1 & 0xff) >> 6);
*out++ = char((c1 & 0x3f) >> 1);
int c2 = *beg++;
*out++ = char((c1 & 0x01) << 4 | (c2 & 0xff) >> 4);
int c3 = *beg++;
*out++ = char((c2 & 0x0f) << 1 | (c3 & 0xff) >> 7);
*out++ = char((c3 & 0x7f) >> 2);
int c4 = *beg++;
*out++ = char((c3 & 0x03) << 3 | (c4 & 0xff) >> 5);
*out++ = char((c4 & 0x1f));
}
switch ( end - beg ) {
case 4: {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 3);
int c1 = *beg++;
*out++ = char((c0 & 0x07) << 2 | (c1 & 0xff) >> 6);
*out++ = char((c1 & 0x3f) >> 1);
int c2 = *beg++;
*out++ = char((c1 & 0x01) << 4 | (c2 & 0xff) >> 4);
int c3 = *beg++;
*out++ = char((c2 & 0x0f) << 1 | (c3 & 0xff) >> 7);
*out++ = char((c3 & 0x7f) >> 2);
*out++ = char((c3 & 0x03) << 3);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
}
return out;
}
case 3: {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 3);
int c1 = *beg++;
*out++ = char((c0 & 0x07) << 2 | (c1 & 0xff) >> 6);
*out++ = char((c1 & 0x3f) >> 1);
int c2 = *beg++;
*out++ = char((c1 & 0x01) << 4 | (c2 & 0xff) >> 4);
*out++ = char((c2 & 0x0f) << 1);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
}
return out;
}
case 2: {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 3);
int c1 = *beg++;
*out++ = char((c0 & 0x07) << 2 | (c1 & 0xff) >> 6);
*out++ = char((c1 & 0x3f) >> 1);
*out++ = char((c1 & 0x01) << 4);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
}
return out;
}
case 1: {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 3);
*out++ = char((c0 & 0x07) << 2);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
*out++ = EOF;
}
return out;
}
default:
return out;
}
}
template < typename I, typename O >
static
O scatter(
I beg,
I end,
O out,
pd_use_t pd_use = pd_invalid )
{
return scatter( beg, end, out, pd_use, typename std::iterator_traits<I>::iterator_category() );
}
template < typename I, typename O >
static
O scatter_standard(
I beg,
I end,
O out )
{
return scatter( beg, end, out, pd_enforce, typename std::iterator_traits<I>::iterator_category() );
}
//
// base<32>::gather -- collect 5-bit chunks into 8-bit characters
//
// If underflow occurs (not enough data collected to output complete last char), then an
// exception will be raised. However, if 'pad' is set, then output will automatically be
// padded, discarding any un-emitted data remaining in previous 5-bit base-32 symbols.
//
// For correct base-32 data produced by 'scatter', this will allow 'gather' to always
// produce the identical output as was originally provided to 'scatter'. However, if simply
// truncated base-32 input is provided (eg. only 1, 3 or 5 symbols of an 8-symbol 40-bit
// base-32 group), then the final 8-bit symbol from the original data will be missing.
//
template < typename I, typename O >
static
O gather(
I beg,
I end,
O out,
pd_use_t pd_use = pd_invalid )
{
while ( beg != end ) {
int c0 = gather_next( beg, end, pd_use, 0 );
int c1 = gather_next( beg, end, pd_use, c0 );
if ( c0 >= 0 and c1 >= 0 )
*out++ = ( ((c0 < 0 ? 0 : c0 ) << 3)
| (c1 < 0 ? 0 : c1 ) >> 2 );
int c2 = gather_next( beg, end, pd_use, c1 );
int c3 = gather_next( beg, end, pd_use, c2 );
if ( c1 >= 0 and c2 >= 0 and c3 >= 0 )
*out++ = ( ((c1 < 0 ? 0 : c1) & 0x03) << 6
| (c2 < 0 ? 0 : c2) << 1
| (c3 < 0 ? 0 : c3) >> 4 );
int c4 = gather_next( beg, end, pd_use, c3 );
if ( c3 >= 0 and c4 >= 0 )
*out++ = ( ((c3 < 0 ? 0 : c3) & 0x0f) << 4
| (c4 < 0 ? 0 : c4) >> 1 );
int c5 = gather_next( beg, end, pd_use, c4 );
int c6 = gather_next( beg, end, pd_use, c5 );
if ( c4 >=0 and c5 >= 0 and c6 >= 0 )
*out++ = ( ((c4 < 0 ? 0 : c4) & 0x01) << 7
| (c5 < 0 ? 0 : c5) << 2
| (c6 < 0 ? 0 : c6) >> 3 );
int c7 = gather_next( beg, end, pd_use, c6 );
if ( c6 >= 0 and c7 >= 0 )
*out++ = ( ((c6 < 0 ? 0 : c6 ) & 0x07) << 5
| (c7 < 0 ? 0 : c7 ));
}
return out;
}
template < typename I, typename O >
static
O gather_standard(
I beg,
I end,
O out )
{
return gather( beg, end, out, pd_enforce );
}
}; // ezpwd::base<32>
//
// ezpwd::serialize::base32...
//
// Shortcut typedefs for the available base32 codecs.
//
typedef base< 32, ezpwd< 32 >>
base32;
typedef base< 32, hex< 32 >>
base32_hex;
typedef base< 32, serialize::standard< 32 >>
base32_standard;
typedef base< 32, crockford< 32 >>
base32_crockford;
//
// ezpwd::serialize::base<64> -- transform individual characters between 6-bit binary and base-64
//
// The char values [0,64) are mapped by base64::encode onto either the
// standard or ezpwd (url-safe) tables:
//
// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ -- RFC4648 standard
// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_ -- RFC4648 standard_url
// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+. -- ezpwd
//
// and base<64>::decode performs the inverse (handles both standard and url-safe encodings).
//
// Any characters encountered outside [0,64) by encode and outside the above set
// by decode raise an exception.
//
template < typename TABLES >
class base< 64, TABLES >
: public base_generic< 64, TABLES > {
public:
using base_generic< 64, TABLES >::encode;
using base_generic< 64, TABLES >::decode;
using base_generic< 64, TABLES >::gather_next;
static constexpr size_t encode_size(
size_t len,
pd_use_t pd_use = pd_invalid )
{
return ( pd_enforce == pd_use
? ( len + 2 ) / 3 * 4
: ( len * 4 + 2 ) / 3 );
}
//
// base<64>::scatter -- distribute a range of input bytes to an output iterator in 6-bit chunks
//
template < typename I, typename O, typename iterator_tag >
static
O scatter(
I beg,
I end,
O out,
pd_use_t pd_use,
iterator_tag )
{
while ( beg != end ) {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 2);
if ( beg == end ) {
*out++ = char((c0 & 0x03) << 4);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
*out++ = EOF;
}
return out;
}
int c1 = *beg++;
*out++ = char((c0 & 0x03) << 4 | (c1 & 0xff) >> 4);
if ( beg == end ) {
*out++ = char((c1 & 0x0f) << 2);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
}
return out;
}
int c2 = *beg++;
*out++ = char((c1 & 0x0f) << 2 | (c2 & 0xff) >> 6);
*out++ = char((c2 & 0x3f));
}
return out;
}
template < typename I, typename O >
static
O scatter(
I beg,
I end,
O out,
pd_use_t pd_use,
std::random_access_iterator_tag )
{
while ( end - beg >= 3 ) {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 2);
int c1 = *beg++;
*out++ = char((c0 & 0x03) << 4 | (c1 & 0xff) >> 4);
int c2 = *beg++;
*out++ = char((c1 & 0x0f) << 2 | (c2 & 0xff) >> 6);
*out++ = char((c2 & 0x3f));
}
switch ( end - beg ) {
case 2: {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 2);
int c1 = *beg++;
*out++ = char((c0 & 0x03) << 4 | (c1 & 0xff) >> 4);
*out++ = char((c1 & 0x0f) << 2);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
}
return out;
}
case 1: {
int c0 = *beg++;
*out++ = char((c0 & 0xff) >> 2);
*out++ = char((c0 & 0x03) << 4);
if ( pd_enforce == pd_use ) {
*out++ = EOF;
*out++ = EOF;
}
return out;
}
default:
return out;
}
}
template < typename I, typename O >
static
O scatter(
I beg,
I end,
O out,
pd_use_t pd_use = pd_invalid )
{
return scatter( beg, end, out, pd_use, typename std::iterator_traits<I>::iterator_category() );
}
template < typename I, typename O >
static
O scatter_standard(
I beg,
I end,
O out )
{
return scatter( beg, end, out, pd_enforce, typename std::iterator_traits<I>::iterator_category() );
}
//
// base<64>::gather -- collect 6-bit chunks into 8-bit characters
//
template < typename I, typename O >
static
O gather(
I beg,
I end,
O out,
pd_use_t pd_use = pd_invalid )
{
while ( beg != end ) {
int c0 = gather_next( beg, end, pd_use, 0 );
int c1 = gather_next( beg, end, pd_use, c0 );
if ( c0 >= 0 and c1 >= 0 )
*out++ = ( ((c0 < 0 ? 0 : c0 ) << 2 )
| (c1 < 0 ? 0 : c1 ) >> 4 );
int c2 = gather_next( beg, end, pd_use, c1 );
if ( c1 >= 0 and c2 >= 0 )
*out++ = ( ((c1 < 0 ? 0 : c1) & 0x0f) << 4
| (c2 < 0 ? 0 : c2) >> 2 );
int c3 = gather_next( beg, end, pd_use, c2 );
if ( c2 >= 0 and c3 >= 0 )
*out++ = ( ((c2 < 0 ? 0 : c2) & 0x03) << 6
| (c3 < 0 ? 0 : c3));
}
return out;
}
template < typename I, typename O >
static
O gather_standard(
I beg,
I end,
O out )
{
return gather( beg, end, out, pd_enforce );
}
}; // ezpwd::serialize::base<64>
//
// ezpwd::serialize::base64...
//
// Shortcut typedefs for the standard base-64 codecs.
//
typedef base< 64, ezpwd< 64 >>
base64;
typedef base< 64, standard< 64 >>
base64_standard;
typedef base< 64, standard_url< 64 >>
base64_standard_url;
typedef base< 64, uuencode< 64 >>
base64_uuencode;
} // namespace ezpwd::serialize
} // namespace ezpwd
#endif // _EZPWD_SERIALIZE