~voidraven/scry

f4368f9a418fb188ed892c658ca32a3057d092d0 — lotus 10 months ago 30e8d2d
Refined range checks for command line arguments, completed cases for different arg combinations. Increased default values. Updated the README file. Added library for converting to base64.
M README.md => README.md +3 -3
@@ 2,11 2,11 @@

[![License](https://is.gd/GbWFMI)](https://opensource.org/licenses/ISC) [![Build Status](https://travis-ci.org/loetus/scry.svg?branch=master)](https://travis-ci.org/loetus/scry)

This tool provides a quick way do generate a [Scrypt](https://en.wikipedia.org/wiki/Scrypt) key from the shell. The normal scrypt cli tool provided [here](https://www.tarsnap.com/scrypt.html) will encrypt and decrypt files for you interactively. But, if you simply want it to give you a key, it cannot. 
This tool provides a quick way do generate a [Scrypt](https://en.wikipedia.org/wiki/Scrypt) key from the shell. The normal scrypt cli tool provided [here](https://www.tarsnap.com/scrypt.html) will encrypt and decrypt files for you interactively. But, if you simply want it to give you a key, it cannot. This tool outputs the resulting key in [MCF format](https://passlib.readthedocs.io/en/stable/modular_crypt_format.html)

### Dependencies
+ [libscrypt-dev](https://github.com/technion/libscrypt)
+ C++14 capable compiler
+ C++14 capable compiler (*e.g.* GCC 4.9 / Clang 3.4 or greater )
+ POSIX [make](https://is.gd/RB94Y8)

### Installation


@@ 39,7 39,7 @@ SuperSecretPassword [enter]

Flags:
-s      # provide your own salt (aka nonce)
-n      # cpu/memory cost (default 4096)
-n      # cpu/memory cost (default 8192)
-r      # block size (default 8)
-p      # parallelism (default 2)
-h      # shows this help info

A include/cppcodec/base32_crockford.hpp => include/cppcodec/base32_crockford.hpp +91 -0
@@ 0,0 1,91 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE32_CROCKFORD
#define CPPCODEC_BASE32_CROCKFORD

#include "detail/codec.hpp"
#include "detail/base32.hpp"

namespace cppcodec {

namespace detail {

static constexpr const char base32_crockford_alphabet[] = {
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // at index 10
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',           // 18 - no I
    'J', 'K',                                         // 20 - no L
    'M', 'N',                                         // 22 - no O
    'P', 'Q', 'R', 'S', 'T',                          // 27 - no U
    'V', 'W', 'X', 'Y', 'Z'                           // 32
};

class base32_crockford_base
{
public:
    static CPPCODEC_ALWAYS_INLINE constexpr size_t alphabet_size() {
        static_assert(sizeof(base32_crockford_alphabet) == 32, "base32 alphabet must have 32 values");
        return sizeof(base32_crockford_alphabet);
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char symbol(alphabet_index_t idx)
    {
        return base32_crockford_alphabet[idx];
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char normalized_symbol(char c)
    {
        // Hex decoding is always case-insensitive (even in RFC 4648), the question
        // is only for encoding whether to use upper-case or lower-case letters.
        return (c == 'O' || c == 'o') ? '0'
            : (c == 'I' || c == 'i' || c == 'L' || c == 'l') ? '1'
            : (c >= 'a' && c <= 'z') ? (c - 'a' + 'A')
            : c;
    }

    static CPPCODEC_ALWAYS_INLINE constexpr bool generates_padding() { return false; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool requires_padding() { return false; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_padding_symbol(char) { return false; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_eof_symbol(char c) { return c == '\0'; }

    static CPPCODEC_ALWAYS_INLINE constexpr bool should_ignore(char c) {
        return c == '-'; // "Hyphens (-) can be inserted into strings [for readability]."
    }
};

// base32_crockford is a concatenative iterative (i.e. streaming) interpretation of Crockford base32.
// It interprets the statement "zero-extend the number to make its bit-length a multiple of 5"
// to mean zero-extending it on the right.
// (The other possible interpretation is base32_crockford_num, a place-based single number encoding system.
// See http://merrigrove.blogspot.ca/2014/04/what-heck-is-base64-encoding-really.html for more info.)
class base32_crockford : public base32_crockford_base
{
public:
    template <typename Codec> using codec_impl = stream_codec<Codec, base32_crockford>;
};

} // namespace detail

using base32_crockford = detail::codec<detail::base32<detail::base32_crockford>>;

} // namespace cppcodec

#endif // CPPCODEC_BASE32_CROCKFORD

A include/cppcodec/base32_default_crockford.hpp => include/cppcodec/base32_default_crockford.hpp +31 -0
@@ 0,0 1,31 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE32_DEFAULT_CROCKFORD
#define CPPCODEC_BASE32_DEFAULT_CROCKFORD

#include "base32_crockford.hpp"

using base32 = cppcodec::base32_crockford;

#endif // CPPCODEC_BASE32_DEFAULT_CROCKFORD

A include/cppcodec/base32_default_hex.hpp => include/cppcodec/base32_default_hex.hpp +31 -0
@@ 0,0 1,31 @@
/**
 *  Copyright (C) 2015, 2016 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE32_DEFAULT_HEX
#define CPPCODEC_BASE32_DEFAULT_HEX

#include "base32_hex.hpp"

using base32 = cppcodec::base32_hex;

#endif // CPPCODEC_BASE32_DEFAULT_HEX

A include/cppcodec/base32_default_rfc4648.hpp => include/cppcodec/base32_default_rfc4648.hpp +31 -0
@@ 0,0 1,31 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE32_DEFAULT_RFC4648
#define CPPCODEC_BASE32_DEFAULT_RFC4648

#include "base32_rfc4648.hpp"

using base32 = cppcodec::base32_rfc4648;

#endif // CPPCODEC_BASE32_DEFAULT_RFC4648

A include/cppcodec/base32_hex.hpp => include/cppcodec/base32_hex.hpp +76 -0
@@ 0,0 1,76 @@
/**
 *  Copyright (C) 2015, 2016 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE32_HEX
#define CPPCODEC_BASE32_HEX

#include "detail/codec.hpp"
#include "detail/base32.hpp"

namespace cppcodec {

namespace detail {

// RFC 4648 uses a simple alphabet: A-Z starting at index 0, then 2-7 starting at index 26.
static constexpr const char base32_hex_alphabet[] = {
    '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'
};

class base32_hex
{
public:
    template <typename Codec> using codec_impl = stream_codec<Codec, base32_hex>;

    static CPPCODEC_ALWAYS_INLINE constexpr size_t alphabet_size() {
        static_assert(sizeof(base32_hex_alphabet) == 32, "base32 alphabet must have 32 values");
        return sizeof(base32_hex_alphabet);
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char symbol(alphabet_index_t idx)
    {
        return base32_hex_alphabet[idx];
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char normalized_symbol(char c)
    {
        // Lower-case letters are accepted, though not generally expected.
        return (c >= 'a' && c <= 'v') ? (c - 'a' + 'A') : c;
    }

    static CPPCODEC_ALWAYS_INLINE constexpr bool generates_padding() { return true; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool requires_padding() { return true; }
    static CPPCODEC_ALWAYS_INLINE constexpr char padding_symbol() { return '='; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_padding_symbol(char c) { return c == '='; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_eof_symbol(char c) { return c == '\0'; }

    // RFC4648 does not specify any whitespace being allowed in base32 encodings.
    static CPPCODEC_ALWAYS_INLINE constexpr bool should_ignore(char) { return false; }
};

} // namespace detail

using base32_hex = detail::codec<detail::base32<detail::base32_hex>>;

} // namespace cppcodec

#endif // CPPCODEC_BASE32_HEX

A include/cppcodec/base32_rfc4648.hpp => include/cppcodec/base32_rfc4648.hpp +76 -0
@@ 0,0 1,76 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE32_RFC4648
#define CPPCODEC_BASE32_RFC4648

#include "detail/codec.hpp"
#include "detail/base32.hpp"

namespace cppcodec {

namespace detail {

// RFC 4648 uses a simple alphabet: A-Z starting at index 0, then 2-7 starting at index 26.
static constexpr const char base32_rfc4648_alphabet[] = {
    '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', // at index 26
    '2', '3', '4', '5', '6', '7'
};

class base32_rfc4648
{
public:
    template <typename Codec> using codec_impl = stream_codec<Codec, base32_rfc4648>;

    static CPPCODEC_ALWAYS_INLINE constexpr size_t alphabet_size() {
        static_assert(sizeof(base32_rfc4648_alphabet) == 32, "base32 alphabet must have 32 values");
        return sizeof(base32_rfc4648_alphabet);
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char symbol(alphabet_index_t idx)
    {
        return base32_rfc4648_alphabet[idx];
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char normalized_symbol(char c)
    {
        // Lower-case letters are accepted, though not generally expected.
        return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c;
    }

    static CPPCODEC_ALWAYS_INLINE constexpr bool generates_padding() { return true; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool requires_padding() { return true; }
    static CPPCODEC_ALWAYS_INLINE constexpr char padding_symbol() { return '='; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_padding_symbol(char c) { return c == '='; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_eof_symbol(char c) { return c == '\0'; }

    // RFC4648 does not specify any whitespace being allowed in base32 encodings.
    static CPPCODEC_ALWAYS_INLINE constexpr bool should_ignore(char) { return false; }
};

} // namespace detail

using base32_rfc4648 = detail::codec<detail::base32<detail::base32_rfc4648>>;

} // namespace cppcodec

#endif // CPPCODEC_BASE32_RFC4648

A include/cppcodec/base64_default_rfc4648.hpp => include/cppcodec/base64_default_rfc4648.hpp +31 -0
@@ 0,0 1,31 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE64_DEFAULT_RFC4648
#define CPPCODEC_BASE64_DEFAULT_RFC4648

#include "base64_rfc4648.hpp"

using base64 = cppcodec::base64_rfc4648;

#endif // CPPCODEC_BASE64_DEFAULT_RFC4648

A include/cppcodec/base64_default_url.hpp => include/cppcodec/base64_default_url.hpp +31 -0
@@ 0,0 1,31 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE64_DEFAULT_URL
#define CPPCODEC_BASE64_DEFAULT_URL

#include "base64_url.hpp"

using base64 = cppcodec::base64_url;

#endif // CPPCODEC_BASE64_DEFAULT_URL

A include/cppcodec/base64_default_url_unpadded.hpp => include/cppcodec/base64_default_url_unpadded.hpp +31 -0
@@ 0,0 1,31 @@
/**
 *  Copyright (C) 2016 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE64_DEFAULT_URL_UNPADDED
#define CPPCODEC_BASE64_DEFAULT_URL_UNPADDED

#include "base64_url_unpadded.hpp"

using base64 = cppcodec::base64_url_unpadded;

#endif // CPPCODEC_BASE64_DEFAULT_URL_UNPADDED

A include/cppcodec/base64_rfc4648.hpp => include/cppcodec/base64_rfc4648.hpp +73 -0
@@ 0,0 1,73 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE64_RFC4648
#define CPPCODEC_BASE64_RFC4648

#include "detail/codec.hpp"
#include "detail/base64.hpp"

namespace cppcodec {

namespace detail {

static constexpr const char base64_rfc4648_alphabet[] = {
    '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', '+', '/'
};

class base64_rfc4648
{
public:
    template <typename Codec> using codec_impl = stream_codec<Codec, base64_rfc4648>;

    static CPPCODEC_ALWAYS_INLINE constexpr size_t alphabet_size() {
        static_assert(sizeof(base64_rfc4648_alphabet) == 64, "base64 alphabet must have 64 values");
        return sizeof(base64_rfc4648_alphabet);
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char symbol(alphabet_index_t idx)
    {
        return base64_rfc4648_alphabet[idx];
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char normalized_symbol(char c) { return c; }

    static CPPCODEC_ALWAYS_INLINE constexpr bool generates_padding() { return true; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool requires_padding() { return true; }
    static CPPCODEC_ALWAYS_INLINE constexpr char padding_symbol() { return '='; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_padding_symbol(char c) { return c == '='; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_eof_symbol(char c) { return c == '\0'; }

    // RFC4648 does not specify any whitespace being allowed in base64 encodings.
    static CPPCODEC_ALWAYS_INLINE constexpr bool should_ignore(char) { return false; }
};

} // namespace detail

using base64_rfc4648 = detail::codec<detail::base64<detail::base64_rfc4648>>;

} // namespace cppcodec

#endif // CPPCODEC_BASE64_RFC4648

A include/cppcodec/base64_url.hpp => include/cppcodec/base64_url.hpp +75 -0
@@ 0,0 1,75 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE64_URL
#define CPPCODEC_BASE64_URL

#include "detail/codec.hpp"
#include "detail/base64.hpp"

namespace cppcodec {

namespace detail {

// The URL and filename safe alphabet is also specified by RFC4648, named "base64url".
// We keep the underscore ("base64_url") for consistency with the other codec variants.
static constexpr const char base64_url_alphabet[] = {
    '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', '-', '_'
};

class base64_url
{
public:
    template <typename Codec> using codec_impl = stream_codec<Codec, base64_url>;

    static CPPCODEC_ALWAYS_INLINE constexpr size_t alphabet_size() {
        static_assert(sizeof(base64_url_alphabet) == 64, "base64 alphabet must have 64 values");
        return sizeof(base64_url_alphabet);
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char symbol(alphabet_index_t idx)
    {
        return base64_url_alphabet[idx];
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char normalized_symbol(char c) { return c; }

    static CPPCODEC_ALWAYS_INLINE constexpr bool generates_padding() { return true; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool requires_padding() { return true; }
    static CPPCODEC_ALWAYS_INLINE constexpr char padding_symbol() { return '='; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_padding_symbol(char c) { return c == '='; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_eof_symbol(char c) { return c == '\0'; }

    // RFC4648 does not specify any whitespace being allowed in base64 encodings.
    static CPPCODEC_ALWAYS_INLINE constexpr bool should_ignore(char) { return false; }
};

} // namespace detail

using base64_url = detail::codec<detail::base64<detail::base64_url>>;

} // namespace cppcodec

#endif // CPPCODEC_BASE64_URL

A include/cppcodec/base64_url_unpadded.hpp => include/cppcodec/base64_url_unpadded.hpp +48 -0
@@ 0,0 1,48 @@
/**
 *  Copyright (C) 2016 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_BASE64_URL_UNPADDED
#define CPPCODEC_BASE64_URL_UNPADDED

#include "base64_url.hpp"

namespace cppcodec {

namespace detail {

class base64_url_unpadded : public base64_url
{
public:
    template <typename Codec> using codec_impl = stream_codec<Codec, base64_url_unpadded>;

    static CPPCODEC_ALWAYS_INLINE constexpr bool generates_padding() { return false; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool requires_padding() { return false; }
};

} // namespace detail

using base64_url_unpadded = detail::codec<detail::base64<detail::base64_url_unpadded>>;

} // namespace cppcodec

#endif // CPPCODEC_BASE64_URL_UNPADDED

A include/cppcodec/data/access.hpp => include/cppcodec/data/access.hpp +328 -0
@@ 0,0 1,328 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  Copyright (C) 2018 Jakob Petsovits
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_DETAIL_DATA_ACCESS
#define CPPCODEC_DETAIL_DATA_ACCESS

#include <stdint.h> // for size_t
#include <string> // for static_assert() checking that string will be optimized
#include <type_traits> // for std::enable_if, std::remove_reference, and such
#include <utility> // for std::declval
#include <vector> // for static_assert() checking that vector will be optimized

#include "../detail/config.hpp" // for CPPCODEC_ALWAYS_INLINE

namespace cppcodec {
namespace data {

// This file contains a number of templated data accessors that can be
// implemented in the cppcodec::data namespace for types that don't fulfill
// the default type requirements:
// For result types: init(Result&, ResultState&, size_t capacity),
//     put(Result&, ResultState&, char), finish(Result&, State&)
// For const (read-only) types: char_data(const T&)
// For both const and result types: size(const T&)

template <typename T>
CPPCODEC_ALWAYS_INLINE size_t size(const T& t) { return t.size(); }

template <typename T, size_t N>
CPPCODEC_ALWAYS_INLINE constexpr size_t size(const T (&t)[N]) noexcept {
    return (void)t, N * sizeof(t[0]);
}

class general_t {};
class specific_t : public general_t {};

class empty_result_state {
    template <typename Result>
    CPPCODEC_ALWAYS_INLINE void size(const Result& result) { return size(result); }
};

// SFINAE: Generic fallback in case no specific state function applies.
template <typename Result>
CPPCODEC_ALWAYS_INLINE empty_result_state create_state(Result&, general_t)
{
    return empty_result_state();
}

//
// Generic templates for containers: Use these init()/put()/finish()
// implementations if no specialization was found.
//

template <typename Result>
CPPCODEC_ALWAYS_INLINE void init(Result& result, empty_result_state&, size_t capacity)
{
    result.resize(0);
    result.reserve(capacity);
}

template <typename Result>
CPPCODEC_ALWAYS_INLINE void finish(Result&, empty_result_state&)
{
    // Default is to push_back(), which already increases the size.
}

// For the put() default implementation, we try calling push_back() with either uint8_t or char,
// whichever compiles. Scary-fancy template magic from http://stackoverflow.com/a/1386390.
namespace fallback {
    struct flag { char c[2]; }; // sizeof > 1
    flag put_uint8(...);

    int operator,(flag, flag);
    template <typename T> void operator,(flag, T&); // map everything else to void
    char operator,(int, flag); // sizeof 1
}

template <typename Result> inline void put_uint8(Result& result, uint8_t c) { result.push_back(c); }

template <bool> struct put_impl;
template <> struct put_impl<true> { // put_uint8() available
    template<typename Result>
    static CPPCODEC_ALWAYS_INLINE void put(Result& result, uint8_t c)
    {
        put_uint8(result, c);
    }
};
template <> struct put_impl<false> { // put_uint8() not available
    template<typename Result>
    static CPPCODEC_ALWAYS_INLINE void put(Result& result, uint8_t c)
    {
        result.push_back(static_cast<char>(c));
    }
};

template <typename Result>
CPPCODEC_ALWAYS_INLINE void put(Result& result, empty_result_state&, uint8_t c)
{
    using namespace fallback;
    put_impl<sizeof(fallback::flag(), put_uint8(result, c), fallback::flag()) != 1>::put(result, c);
}

//
// Specialization for container types with direct mutable data access,
// e.g. std::vector<uint8_t>.
//
// The expected way to specialize is to draft a new xyz_result_state type and
// return an instance of it from a create_state() template specialization.
// You can then create overloads for init(), put() and finish()
// for the new result state type.
//
// If desired, a non-templated overload for both specific types
// (result & state) can be added to tailor it to that particular result type.
//

template <typename T>
constexpr auto data_is_mutable(T* t) -> decltype(t->data()[size_t(0)] = 'x', bool())
{
    return (void)t, true;
}
constexpr bool data_is_mutable(...) { return false; }

template <typename Result>
class direct_data_access_result_state
{
public:
    CPPCODEC_ALWAYS_INLINE void init(Result& result, size_t capacity)
    {
        // reserve() may not actually allocate the storage right away,
        // and it isn't guaranteed that it will be untouched upon the
        //.next resize(). In that light, resize from the start and
        // slightly reduce the size at the end if necessary.
        result.resize(capacity);

        // result.data() may perform a calculation to retrieve the address.
        // E.g. std::string (since C++11) will use small string optimization,
        // so it needs to check if it's using allocated data or (ab)using
        // its own member variables interpreted as char array.
        // (This result_state is used for std::string starting with C++17.)
        // Conditional code paths are slow so we only do it once, at the start.
        m_buffer = result.data();
    }
    CPPCODEC_ALWAYS_INLINE void put(Result&, char c)
    {
        m_buffer[m_offset++] = c;
    }
    CPPCODEC_ALWAYS_INLINE void finish(Result& result)
    {
        result.resize(m_offset);
    }
    CPPCODEC_ALWAYS_INLINE size_t size(const Result&)
    {
        return m_offset;
    }
private:
    // Make sure to get the mutable buffer decltype by using assignment.
    typename std::remove_reference<
            decltype(std::declval<Result>().data()[size_t(0)] = 'x')>::type* m_buffer;
    size_t m_offset = 0;
};

// SFINAE: Select a specific state based on the result type and possible result state type.
// Implement this if direct data access (`result.data()[0] = 'x') isn't already possible
// and you want to specialize it for your own result type.
// Note: The enable_if should ideally be part of the class declaration,
//       but Visual Studio C++ will not compile it that way.
//       Have it here in the factory function instead.
template <typename Result,
          typename = typename std::enable_if<
                  data_is_mutable(static_cast<Result*>(nullptr))>::type>
CPPCODEC_ALWAYS_INLINE direct_data_access_result_state<Result> create_state(Result&, specific_t)
{
    return direct_data_access_result_state<Result>();
}

static_assert(std::is_same<
        decltype(create_state(*static_cast<std::vector<uint8_t>*>(nullptr), specific_t())),
        direct_data_access_result_state<std::vector<uint8_t>>>::value,
        "std::vector<uint8_t> must be handled by direct_data_access_result_state");

// Specialized init(), put() and finish() functions for direct_data_access_result_state.
template <typename Result>
CPPCODEC_ALWAYS_INLINE void init(Result& result, direct_data_access_result_state<Result>& state, size_t capacity)
{
    state.init(result, capacity);
}

template <typename Result>
CPPCODEC_ALWAYS_INLINE void put(Result& result, direct_data_access_result_state<Result>& state, char c)
{
    state.put(result, c);
}

template <typename Result>
CPPCODEC_ALWAYS_INLINE void finish(Result& result, direct_data_access_result_state<Result>& state)
{
    state.finish(result);
}

//
// Specialization for container types with direct mutable array access,
// e.g. std::string. This is generally faster because bound checks are
// minimal and operator[] is more likely noexcept. In addition,
// std::string::push_back() needs to write a null character on every
// expansion, which should be more efficient when done in bulk by resize().
//
// Compared to the above, tracking an extra offset variable is cheap.
//

template <typename T>
constexpr auto array_access_is_mutable(T* t) -> decltype((*t)[size_t(0)] = 'x', bool())
{
    return (void)t, true;
}
constexpr bool array_access_is_mutable(...) { return false; }

template <typename Result>
class array_access_result_state
{
public:
    CPPCODEC_ALWAYS_INLINE void init(Result& result, size_t capacity)
    {
        // reserve() may not actually allocate the storage right away,
        // and it isn't guaranteed that it will be untouched upon the
        //.next resize(). In that light, resize from the start and
        // slightly reduce the size at the end if necessary.
        result.resize(capacity);
    }
    CPPCODEC_ALWAYS_INLINE void put(Result& result, char c)
    {
        result[m_offset++] = c;
    }
    CPPCODEC_ALWAYS_INLINE void finish(Result& result)
    {
        result.resize(m_offset);
    }
    CPPCODEC_ALWAYS_INLINE size_t size(const Result&)
    {
        return m_offset;
    }
private:
    size_t m_offset = 0;
};

// SFINAE: Select a specific state based on the result type and possible result state type.
// Note: The enable_if should ideally be part of the class declaration,
//       but Visual Studio C++ will not compile it that way.
//       Have it here in the factory function instead.
template <typename Result,
          typename = typename std::enable_if<
                  !data_is_mutable(static_cast<Result*>(nullptr)) // no more than one template option
                  && array_access_is_mutable(static_cast<Result*>(nullptr))>::type>
CPPCODEC_ALWAYS_INLINE array_access_result_state<Result> create_state(Result&, specific_t)
{
    return array_access_result_state<Result>();
}

#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L)
static_assert(std::is_same<
    decltype(create_state(*static_cast<std::string*>(nullptr), specific_t())),
    direct_data_access_result_state<std::string>>::value,
    "std::string (C++17 and later) must be handled by direct_data_access_result_state");
#elif __cplusplus < 201703 && !defined(_MSVC_LANG) // we can't trust MSVC to set this right
static_assert(std::is_same<
        decltype(create_state(*static_cast<std::string*>(nullptr), specific_t())),
        array_access_result_state<std::string>>::value,
        "std::string (pre-C++17) must be handled by array_access_result_state");
#endif

// Specialized init(), put() and finish() functions for array_access_result_state.
template <typename Result>
CPPCODEC_ALWAYS_INLINE void init(Result& result, array_access_result_state<Result>& state, size_t capacity)
{
    state.init(result, capacity);
}

template <typename Result>
CPPCODEC_ALWAYS_INLINE void put(Result& result, array_access_result_state<Result>& state, char c)
{
    state.put(result, c);
}

template <typename Result>
CPPCODEC_ALWAYS_INLINE void finish(Result& result, array_access_result_state<Result>& state)
{
    state.finish(result);
}

// char_data() is only used to read, not for result buffers.
template <typename T> inline const char* char_data(const T& t)
{
    return reinterpret_cast<const char*>(t.data());
}
template <typename T, size_t N> inline const char* char_data(const T (&t)[N]) noexcept
{
    return reinterpret_cast<const char*>(&(t[0]));
}

template <typename T> inline const uint8_t* uchar_data(const T& t)
{
    return reinterpret_cast<const uint8_t*>(char_data(t));
}

} // namespace data
} // namespace cppcodec

#endif

A include/cppcodec/data/raw_result_buffer.hpp => include/cppcodec/data/raw_result_buffer.hpp +71 -0
@@ 0,0 1,71 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_DETAIL_RAW_RESULT_BUFFER
#define CPPCODEC_DETAIL_RAW_RESULT_BUFFER

#include <stdint.h> // for size_t
#include <stdlib.h> // for abort()

#include "access.hpp"

namespace cppcodec {
namespace data {

class raw_result_buffer
{
public:
    raw_result_buffer(char* data, size_t capacity)
        : m_ptr(data + capacity)
        , m_begin(data)
    {
    }

    char last() const { return *(m_ptr - 1); }
    void push_back(char c) { *m_ptr = c; ++m_ptr; }
    size_t size() const { return m_ptr - m_begin; }
    void resize(size_t size) { m_ptr = m_begin + size; }

private:
    char* m_ptr;
    char* m_begin;
};


template <> inline void init<raw_result_buffer>(
        raw_result_buffer& result, empty_result_state&, size_t capacity)
{
    // This version of init() doesn't do a reserve(), and instead checks whether the
    // initial size (capacity) is enough before resizing to 0.
    // The codec is expected not to exceed this capacity.
    if (capacity > result.size()) {
        abort();
    }
    result.resize(0);
}
template <> inline void finish<raw_result_buffer>(raw_result_buffer&, empty_result_state&) { }

} // namespace data
} // namespace cppcodec

#endif

A include/cppcodec/detail/base32.hpp => include/cppcodec/detail/base32.hpp +166 -0
@@ 0,0 1,166 @@
/**
 *  Copyright (C) 2015 Trustifier Inc.
 *  Copyright (C) 2015 Ahmed Masud
 *  Copyright (C) 2015 Topology LP
 *  Copyright (C) 2018 Jakob Petsovits
 *  All rights reserved.
 *
 *  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.
 *
 *  Adapted from https://github.com/ahmed-masud/libbase32,
 *  commit 79761b2b79b0545697945efe0987a8d3004512f9.
 *  Quite different now.
 */

#ifndef CPPCODEC_DETAIL_BASE32
#define CPPCODEC_DETAIL_BASE32

#include <stdint.h>
#include <stdlib.h> // for abort()

#include "../data/access.hpp"
#include "../parse_error.hpp"
#include "config.hpp"
#include "stream_codec.hpp"

namespace cppcodec {
namespace detail {

template <typename CodecVariant>
class base32 : public CodecVariant::template codec_impl<base32<CodecVariant>>
{
public:
    static inline constexpr uint8_t binary_block_size() { return 5; }
    static inline constexpr uint8_t encoded_block_size() { return 8; }

    static CPPCODEC_ALWAYS_INLINE constexpr uint8_t num_encoded_tail_symbols(uint8_t num_bytes)
    {
        return (num_bytes == 1) ? 2    // 2 symbols, 6 padding characters
                : (num_bytes == 2) ? 4 // 4 symbols, 4 padding characters
                : (num_bytes == 3) ? 5 // 5 symbols, 3 padding characters
                : (num_bytes == 4) ? 7 // 7 symbols, 1 padding characters
                : throw std::domain_error("invalid number of bytes in a tail block");
    }

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE constexpr uint8_t index(
            const uint8_t* b /*binary block*/) noexcept
    {
        static_assert(I >= 0 && I < encoded_block_size(),
                "invalid encoding symbol index in a block");

        return (I == 0) ? ((b[0] >> 3) & 0x1F) // first 5 bits
                : (I == 1) ? (((b[0] << 2) & 0x1C) | ((b[1] >> 6) & 0x3))
                : (I == 2) ? ((b[1] >> 1) & 0x1F)
                : (I == 3) ? (((b[1] << 4) & 0x10) | ((b[2] >> 4) & 0xF))
                : (I == 4) ? (((b[2] << 1) & 0x1E) | ((b[3] >> 7) & 0x1))
                : (I == 5) ? ((b[3] >> 2) & 0x1F)
                : (I == 6) ? (((b[3] << 3) & 0x18) | ((b[4] >> 5) & 0x7))
                : /*I == 7*/ (b[4] & 0x1F); // last 5 bits;
    }

    template <bool B>
    using uint8_if = typename std::enable_if<B, uint8_t>::type;

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE constexpr
    uint8_if<I == 1 || I == 3 || I == 4 || I == 6> index_last(
            const uint8_t* b /*binary block*/) noexcept
    {
        return (I == 1) ? ((b[0] << 2) & 0x1C)     // abbreviated 2nd symbol
                : (I == 3) ? ((b[1] << 4) & 0x10)  // abbreviated 4th symbol
                : (I == 4) ? ((b[2] << 1) & 0x1E)  // abbreviated 5th symbol
                : /*I == 6*/ ((b[3] << 3) & 0x18); // abbreviated 7th symbol
    }

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE
    uint8_if<I != 1 && I != 3 && I != 4 && I != 6> index_last(
            const uint8_t* /*binary block*/)
    {
        throw std::domain_error("invalid last encoding symbol index in a tail");
    }

    template <typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void decode_block(
            Result& decoded, ResultState&, const alphabet_index_t* idx);

    template <typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void decode_tail(
            Result& decoded, ResultState&, const alphabet_index_t* idx, size_t idx_len);
};

//
//     11111111 10101010 10110011  10111100 10010100
// => 11111 11110 10101 01011 00111 01111 00100 10100
//

template <typename CodecVariant>
template <typename Result, typename ResultState>
CPPCODEC_ALWAYS_INLINE void base32<CodecVariant>::decode_block(
        Result& decoded, ResultState& state, const alphabet_index_t* idx)
{
    put(decoded, state, static_cast<uint8_t>(((idx[0] << 3) & 0xF8) | ((idx[1] >> 2) & 0x7)));
    put(decoded, state, static_cast<uint8_t>(((idx[1] << 6) & 0xC0) | ((idx[2] << 1) & 0x3E) | ((idx[3] >> 4) & 0x1)));
    put(decoded, state, static_cast<uint8_t>(((idx[3] << 4) & 0xF0) | ((idx[4] >> 1) & 0xF)));
    put(decoded, state, static_cast<uint8_t>(((idx[4] << 7) & 0x80) | ((idx[5] << 2) & 0x7C) | ((idx[6] >> 3) & 0x3)));
    put(decoded, state, static_cast<uint8_t>(((idx[6] << 5) & 0xE0) | (idx[7] & 0x1F)));
}

template <typename CodecVariant>
template <typename Result, typename ResultState>
CPPCODEC_ALWAYS_INLINE void base32<CodecVariant>::decode_tail(
        Result& decoded, ResultState& state, const alphabet_index_t* idx, size_t idx_len)
{
    if (idx_len == 1) {
        throw invalid_input_length(
                "invalid number of symbols in last base32 block: found 1, expected 2, 4, 5 or 7");
    }
    if (idx_len == 3) {
        throw invalid_input_length(
                "invalid number of symbols in last base32 block: found 3, expected 2, 4, 5 or 7");
    }
    if (idx_len == 6) {
        throw invalid_input_length(
                "invalid number of symbols in last base32 block: found 6, expected 2, 4, 5 or 7");
    }

    // idx_len == 2: decoded size 1
    put(decoded, state, static_cast<uint8_t>(((idx[0] << 3) & 0xF8) | ((idx[1] >> 2) & 0x7)));
    if (idx_len == 2) {
        return;
    }
    // idx_len == 4: decoded size 2
    put(decoded, state, static_cast<uint8_t>(((idx[1] << 6) & 0xC0) | ((idx[2] << 1) & 0x3E) | ((idx[3] >> 4) & 0x1)));
    if (idx_len == 4) {
        return;
    }
    // idx_len == 5: decoded size 3
    put(decoded, state, static_cast<uint8_t>(((idx[3] << 4) & 0xF0) | ((idx[4] >> 1) & 0xF)));
    if (idx_len == 5) {
        return;
    }
    // idx_len == 7: decoded size 4
    put(decoded, state, static_cast<uint8_t>(((idx[4] << 7) & 0x80) | ((idx[5] << 2) & 0x7C) | ((idx[6] >> 3) & 0x3)));
}

} // namespace detail
} // namespace cppcodec

#endif // CPPCODEC_DETAIL_BASE32

A include/cppcodec/detail/base64.hpp => include/cppcodec/detail/base64.hpp +132 -0
@@ 0,0 1,132 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  Copyright (C) 2013 Adam Rudd (bit calculations)
 *  Copyright (C) 2018 Jakob Petsovits
 *  All rights reserved.
 *
 *  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.
 *
 *  Bit calculations adapted from https://github.com/adamvr/arduino-base64,
 *  commit 999595783185a0afcba156d7276dfeaa9cb5382f.
 */

#ifndef CPPCODEC_DETAIL_BASE64
#define CPPCODEC_DETAIL_BASE64

#include <stdexcept>
#include <stdint.h>

#include "../data/access.hpp"
#include "../parse_error.hpp"
#include "config.hpp"
#include "stream_codec.hpp"

namespace cppcodec {
namespace detail {

template <typename CodecVariant>
class base64 : public CodecVariant::template codec_impl<base64<CodecVariant>>
{
public:
    static inline constexpr uint8_t binary_block_size() { return 3; }
    static inline constexpr uint8_t encoded_block_size() { return 4; }

    static CPPCODEC_ALWAYS_INLINE constexpr uint8_t num_encoded_tail_symbols(uint8_t num_bytes)
    {
        return (num_bytes == 1) ? 2    // 2 symbols, 2 padding characters
                : (num_bytes == 2) ? 3 // 3 symbols, 1 padding character
                : throw std::domain_error("invalid number of bytes in a tail block");
    }

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE constexpr uint8_t index(
            const uint8_t* b /*binary block*/) noexcept
    {
        static_assert(I >= 0 && I < encoded_block_size(),
                "invalid encoding symbol index in a block");

        return (I == 0) ? (b[0] >> 2) // first 6 bits
                : (I == 1) ? (((b[0] & 0x3) << 4) | (b[1] >> 4))
                : (I == 2) ? (((b[1] & 0xF) << 2) | (b[2] >> 6))
                : /*I == 3*/ (b[2] & 0x3F); // last 6 bits
    }

    template <bool B>
    using uint8_if = typename std::enable_if<B, uint8_t>::type;

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE constexpr uint8_if<I == 1 || I == 2> index_last(
            const uint8_t* b /*binary block*/) noexcept
    {
        return (I == 1) ? ((b[0] & 0x3) << 4)     // abbreviated 2nd symbol
                : /*I == 2*/ ((b[1] & 0xF) << 2); // abbreviated 3rd symbol
    }

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE uint8_if<I != 1 && I != 2> index_last(
            const uint8_t* /*binary block*/)
    {
        throw std::domain_error("invalid last encoding symbol index in a tail");
    }

    template <typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void decode_block(
            Result& decoded, ResultState&, const alphabet_index_t* idx);

    template <typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void decode_tail(
            Result& decoded, ResultState&, const alphabet_index_t* idx, size_t idx_len);
};


template <typename CodecVariant>
template <typename Result, typename ResultState>
CPPCODEC_ALWAYS_INLINE void base64<CodecVariant>::decode_block(
        Result& decoded, ResultState& state, const alphabet_index_t* idx)
{
    uint_fast32_t dec = (idx[0] << 18) | (idx[1] << 12) | (idx[2] << 6) | idx[3];
    data::put(decoded, state, static_cast<uint8_t>(dec >> 16));
    data::put(decoded, state, static_cast<uint8_t>((dec >> 8) & 0xFF));
    data::put(decoded, state, static_cast<uint8_t>(dec & 0xFF));
}

template <typename CodecVariant>
template <typename Result, typename ResultState>
CPPCODEC_ALWAYS_INLINE void base64<CodecVariant>::decode_tail(
        Result& decoded, ResultState& state, const alphabet_index_t* idx, size_t idx_len)
{
    if (idx_len == 1) {
        throw invalid_input_length(
                "invalid number of symbols in last base64 block: found 1, expected 2 or 3");
    }

    // idx_len == 2: decoded size 1
    data::put(decoded, state, static_cast<uint8_t>((idx[0] << 2) + ((idx[1] & 0x30) >> 4)));
    if (idx_len == 2) {
        return;
    }

    // idx_len == 3: decoded size 2
    data::put(decoded, state, static_cast<uint8_t>(((idx[1] & 0xF) << 4) + ((idx[2] & 0x3C) >> 2)));
}

} // namespace detail
} // namespace cppcodec

#endif // CPPCODEC_DETAIL_BASE64

A include/cppcodec/detail/codec.hpp => include/cppcodec/detail/codec.hpp +327 -0
@@ 0,0 1,327 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_DETAIL_CODEC
#define CPPCODEC_DETAIL_CODEC

#include <assert.h>
#include <stdint.h>
#include <string>
#include <vector>

#include "../data/access.hpp"
#include "../data/raw_result_buffer.hpp"

namespace cppcodec {
namespace detail {

// SFINAE: Templates sometimes beat sensible overloads - make sure we don't call the wrong one.
template <typename T>
struct non_numeric : std::enable_if<!std::is_arithmetic<T>::value> { };


/**
 * Public interface for all the codecs. For API documentation, see README.md.
 */
template <typename CodecImpl>
class codec
{
public:
    //
    // Encoding

    // Convenient version, returns an std::string.
    static std::string encode(const uint8_t* binary, size_t binary_size);
    static std::string encode(const char* binary, size_t binary_size);
    // static std::string encode(const T& binary); -> provided by template below

    // Convenient version with templated result type.
    template <typename Result> static Result encode(const uint8_t* binary, size_t binary_size);
    template <typename Result> static Result encode(const char* binary, size_t binary_size);
    template <typename Result = std::string, typename T = std::vector<uint8_t>>
    static Result encode(const T& binary);

    // Reused result container version. Resizes encoded_result before writing to it.
    template <typename Result>
    static void encode(Result& encoded_result, const uint8_t* binary, size_t binary_size);
    template <typename Result>
    static void encode(Result& encoded_result, const char* binary, size_t binary_size);
    template <typename Result, typename T, typename non_numeric<T>::type* = nullptr>
    static void encode(Result& encoded_result, const T& binary);

    // Raw pointer output, assumes pre-allocated memory with size > encoded_size(binary_size).
    static size_t encode(
            char* encoded_result, size_t encoded_buffer_size,
            const uint8_t* binary, size_t binary_size) noexcept;
    static size_t encode(
            char* encoded_result, size_t encoded_buffer_size,
            const char* binary, size_t binary_size) noexcept;
    template<typename T>
    static size_t encode(
            char* encoded_result, size_t encoded_buffer_size,
            const T& binary) noexcept;

    // Calculate the exact length of the encoded string based on binary size.
    static constexpr size_t encoded_size(size_t binary_size) noexcept;

    //
    // Decoding

    // Convenient version, returns an std::vector<uint8_t>.
    static std::vector<uint8_t> decode(const char* encoded, size_t encoded_size);
    // static std::vector<uint8_t> decode(const T& encoded); -> provided by template below

    // Convenient version with templated result type.
    template <typename Result> static Result decode(const char* encoded, size_t encoded_size);
    template <typename Result = std::vector<uint8_t>, typename T = std::string>
    static Result decode(const T& encoded);

    // Reused result container version. Resizes binary_result before writing to it.
    template <typename Result>
    static void decode(Result& binary_result, const char* encoded, size_t encoded_size);
    template <typename Result, typename T, typename non_numeric<T>::type* = nullptr>
    static void decode(Result& binary_result, const T& encoded);

    // Raw pointer output, assumes pre-allocated memory with size > decoded_max_size(encoded_size).
    static size_t decode(
            uint8_t* binary_result, size_t binary_buffer_size,
            const char* encoded, size_t encoded_size);
    static size_t decode(
            char* binary_result, size_t binary_buffer_size,
            const char* encoded, size_t encoded_size);
    template<typename T> static size_t decode(
            uint8_t* binary_result, size_t binary_buffer_size, const T& encoded);
    template<typename T> static size_t decode(
            char* binary_result, size_t binary_buffer_size, const T& encoded);

    // Calculate the maximum size of the decoded binary buffer based on the encoded string length.
    static constexpr size_t decoded_max_size(size_t encoded_size) noexcept;
};


//
// Inline definitions of the above functions, using CRTP to call into CodecImpl
//

//
// Encoding

template <typename CodecImpl>
inline std::string codec<CodecImpl>::encode(const uint8_t* binary, size_t binary_size)
{
    return encode<std::string>(binary, binary_size);
}

template <typename CodecImpl>
inline std::string codec<CodecImpl>::encode(const char* binary, size_t binary_size)
{
    return encode<std::string>(reinterpret_cast<const uint8_t*>(binary), binary_size);
}

template <typename CodecImpl>
template <typename Result>
inline Result codec<CodecImpl>::encode(const uint8_t* binary, size_t binary_size)
{
    Result encoded_result;
    encode(encoded_result, binary, binary_size);
    return encoded_result;
}

template <typename CodecImpl>
template <typename Result>
inline Result codec<CodecImpl>::encode(const char* binary, size_t binary_size)
{
    return encode<Result>(reinterpret_cast<const uint8_t*>(binary), binary_size);
}

template <typename CodecImpl>
template <typename Result, typename T>
inline Result codec<CodecImpl>::encode(const T& binary)
{
    return encode<Result>(data::uchar_data(binary), data::size(binary));
}

template <typename CodecImpl>
template <typename Result>
inline void codec<CodecImpl>::encode(
    Result& encoded_result, const uint8_t* binary, size_t binary_size)
{
    // This overload is where we reserve buffer capacity and call into CodecImpl.
    size_t encoded_buffer_size = encoded_size(binary_size);
    auto state = data::create_state(encoded_result, data::specific_t());
    data::init(encoded_result, state, encoded_buffer_size);

    CodecImpl::encode(encoded_result, state, binary, binary_size);
    data::finish(encoded_result, state);
    assert(data::size(encoded_result) == encoded_buffer_size);
}

template <typename CodecImpl>
template <typename Result>
inline void codec<CodecImpl>::encode(
    Result& encoded_result, const char* binary, size_t binary_size)
{
    encode(encoded_result, reinterpret_cast<const uint8_t*>(binary), binary_size);
}

template <typename CodecImpl>
template <typename Result, typename T, typename non_numeric<T>::type*>
inline void codec<CodecImpl>::encode(Result& encoded_result, const T& binary)
{
    encode(encoded_result, data::uchar_data(binary), data::size(binary));
}

template <typename CodecImpl>
inline size_t codec<CodecImpl>::encode(
        char* encoded_result, size_t encoded_buffer_size,
        const uint8_t* binary, size_t binary_size) noexcept
{
    // This overload is where we wrap the result pointer & size.
    data::raw_result_buffer encoded(encoded_result, encoded_buffer_size);
    encode(encoded, binary, binary_size);

    size_t encoded_size = data::size(encoded);
    if (encoded_size < encoded_buffer_size) {
        encoded_result[encoded_size] = '\0';
    }
    return encoded_size;
}

template <typename CodecImpl>
inline size_t codec<CodecImpl>::encode(
        char* encoded_result, size_t encoded_buffer_size,
        const char* binary, size_t binary_size) noexcept
{
    // This overload is where we wrap the result pointer & size.
    return encode(encoded_result, encoded_buffer_size,
            reinterpret_cast<const uint8_t*>(binary), binary_size);
}

template <typename CodecImpl>
template <typename T>
inline size_t codec<CodecImpl>::encode(
        char* encoded_result, size_t encoded_buffer_size,
        const T& binary) noexcept
{
    return encode(encoded_result, encoded_buffer_size, data::uchar_data(binary), data::size(binary));
}

template <typename CodecImpl>
inline constexpr size_t codec<CodecImpl>::encoded_size(size_t binary_size) noexcept
{
    return CodecImpl::encoded_size(binary_size);
}


//
// Decoding

template <typename CodecImpl>
inline std::vector<uint8_t> codec<CodecImpl>::decode(const char* encoded, size_t encoded_size)
{
    return decode<std::vector<uint8_t>>(encoded, encoded_size);
}

template <typename CodecImpl>
template <typename Result>
inline Result codec<CodecImpl>::decode(const char* encoded, size_t encoded_size)
{
    Result result;
    decode(result, encoded, encoded_size);
    return result;
}

template <typename CodecImpl>
template <typename Result, typename T>
inline Result codec<CodecImpl>::decode(const T& encoded)
{
    return decode<Result>(data::char_data(encoded), data::size(encoded));
}

template <typename CodecImpl>
template <typename Result>
inline void codec<CodecImpl>::decode(Result& binary_result, const char* encoded, size_t encoded_size)
{
    // This overload is where we reserve buffer capacity and call into CodecImpl.
    size_t binary_buffer_size = decoded_max_size(encoded_size);
    auto state = data::create_state(binary_result, data::specific_t());
    data::init(binary_result, state, binary_buffer_size);

    CodecImpl::decode(binary_result, state, encoded, encoded_size);
    data::finish(binary_result, state);
    assert(data::size(binary_result) <= binary_buffer_size);
}


template <typename CodecImpl>
template <typename Result, typename T, typename non_numeric<T>::type*>
inline void codec<CodecImpl>::decode(Result& binary_result, const T& encoded)
{
    decode(binary_result, data::char_data(encoded), data::size(encoded));
}

template <typename CodecImpl>
inline size_t codec<CodecImpl>::decode(
        uint8_t* binary_result, size_t binary_buffer_size,
        const char* encoded, size_t encoded_size)
{
    return decode(reinterpret_cast<char*>(binary_result), binary_buffer_size, encoded, encoded_size);
}

template <typename CodecImpl>
inline size_t codec<CodecImpl>::decode(
        char* binary_result, size_t binary_buffer_size,
        const char* encoded, size_t encoded_size)
{
    // This overload is where we wrap the result pointer & size.
    data::raw_result_buffer binary(binary_result, binary_buffer_size);
    decode(binary, encoded, encoded_size);
    return data::size(binary);
}

template <typename CodecImpl>
template <typename T>
inline size_t codec<CodecImpl>::decode(
        uint8_t* binary_result, size_t binary_buffer_size, const T& encoded)
{
    return decode(reinterpret_cast<char*>(binary_result), binary_buffer_size, encoded);
}

template <typename CodecImpl>
template <typename T>
inline size_t codec<CodecImpl>::decode(char* binary_result, size_t binary_buffer_size, const T& encoded)
{
    return decode(binary_result, binary_buffer_size, data::char_data(encoded), data::size(encoded));
}

template <typename CodecImpl>
inline constexpr size_t codec<CodecImpl>::decoded_max_size(size_t encoded_size) noexcept
{
    return CodecImpl::decoded_max_size(encoded_size);
}


} // namespace detail
} // namespace cppcodec

#endif

A include/cppcodec/detail/config.hpp => include/cppcodec/detail/config.hpp +40 -0
@@ 0,0 1,40 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_DETAIL_CONFIG_HPP
#define CPPCODEC_DETAIL_CONFIG_HPP

#ifndef __has_attribute
  #define __has_attribute(x) 0
#endif

#if __GNUC__ || __has_attribute(always_inline)
#define CPPCODEC_ALWAYS_INLINE inline __attribute__((always_inline))
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#define CPPCODEC_ALWAYS_INLINE inline __forceinline
#else
#define CPPCODEC_ALWAYS_INLINE inline
#endif

#endif // CPPCODEC_DETAIL_CONFIG_HPP


A include/cppcodec/detail/hex.hpp => include/cppcodec/detail/hex.hpp +114 -0
@@ 0,0 1,114 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  Copyright (C) 2018 Jakob Petsovits
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_DETAIL_HEX
#define CPPCODEC_DETAIL_HEX

#include <stdint.h>
#include <stdlib.h> // for abort()

#include "../data/access.hpp"
#include "../parse_error.hpp"
#include "stream_codec.hpp"

namespace cppcodec {
namespace detail {

template <typename CodecVariant>
class hex : public CodecVariant::template codec_impl<hex<CodecVariant>>
{
public:
    static inline constexpr uint8_t binary_block_size() { return 1; }
    static inline constexpr uint8_t encoded_block_size() { return 2; }

    static CPPCODEC_ALWAYS_INLINE constexpr uint8_t num_encoded_tail_symbols(uint8_t /*num_bytes*/) noexcept
    {
        // Hex encoding only works on full bytes so there are no tails,
        // no padding characters, and this function should (must) never be called.
        return 0;
    }

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE constexpr uint8_t index(
            const uint8_t* b /*binary block*/) noexcept
    {
        static_assert(I >= 0 && I < encoded_block_size(),
                "invalid encoding symbol index in a block");

        return (I == 0) ? (b[0] >> 4) // first 4 bits
                : /*I == 1*/ (b[0] & 0xF); // last 4 bits
    }

    // With only 2 bytes, enc<1> will always result in a full index() call and
    // enc<0> will be protected by a not-reached assertion, so we don't actually
    // care about index_last() except optimizing it out as good as possible.
    template <bool B>
    using uint8_if = typename std::enable_if<B, uint8_t>::type;

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE constexpr uint8_if<I == 0> index_last(
            const uint8_t* /*binary block*/) noexcept
    {
        return 0;
    }

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE uint8_if<I != 0> index_last(
            const uint8_t* /*binary block*/)
    {
        throw std::domain_error("invalid last encoding symbol index in a tail");
    }

    template <typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void decode_block(
            Result& decoded, ResultState&, const alphabet_index_t* idx);

    template <typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void decode_tail(
            Result& decoded, ResultState&, const alphabet_index_t* idx, size_t idx_len);
};


template <typename CodecVariant>
template <typename Result, typename ResultState>
CPPCODEC_ALWAYS_INLINE void hex<CodecVariant>::decode_block(
        Result& decoded, ResultState& state, const alphabet_index_t* idx)
{
    data::put(decoded, state, static_cast<uint8_t>((idx[0] << 4) | idx[1]));
}

template <typename CodecVariant>
template <typename Result, typename ResultState>
CPPCODEC_ALWAYS_INLINE void hex<CodecVariant>::decode_tail(
        Result&, ResultState&, const alphabet_index_t*, size_t)
{
    throw invalid_input_length(
            "odd-length hex input is not supported by the streaming octet decoder, "
            "use a place-based number decoder instead");
}

} // namespace detail
} // namespace cppcodec

#endif // CPPCODEC_DETAIL_HEX

A include/cppcodec/detail/stream_codec.hpp => include/cppcodec/detail/stream_codec.hpp +439 -0
@@ 0,0 1,439 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  Copyright (C) 2018 Jakob Petsovits
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_DETAIL_STREAM_CODEC
#define CPPCODEC_DETAIL_STREAM_CODEC

#include <limits>
#include <stdlib.h> // for abort()
#include <stdint.h>

#include "../parse_error.hpp"
#include "config.hpp"

namespace cppcodec {
namespace detail {

using alphabet_index_t = uint_fast16_t;

template <typename Codec, typename CodecVariant>
class stream_codec
{
public:
    template <typename Result, typename ResultState> static void encode(
            Result& encoded_result, ResultState&, const uint8_t* binary, size_t binary_size);

    template <typename Result, typename ResultState> static void decode(
            Result& binary_result, ResultState&, const char* encoded, size_t encoded_size);

    static constexpr size_t encoded_size(size_t binary_size) noexcept;
    static constexpr size_t decoded_max_size(size_t encoded_size) noexcept;
};

template <bool GeneratesPadding> // default for CodecVariant::generates_padding() == false
struct padder {
    template <typename CodecVariant, typename Result, typename ResultState, typename EncodedBlockSizeT>
    CPPCODEC_ALWAYS_INLINE void operator()(Result&, ResultState&, EncodedBlockSizeT) { }
};

template<> // specialization for CodecVariant::generates_padding() == true
struct padder<true> {
    template <typename CodecVariant, typename Result, typename ResultState, typename EncodedBlockSizeT>
    CPPCODEC_ALWAYS_INLINE void operator()(
            Result& encoded, ResultState& state, EncodedBlockSizeT num_padding_characters)
    {
        for (EncodedBlockSizeT i = 0; i < num_padding_characters; ++i) {
            data::put(encoded, state, CodecVariant::padding_symbol());
        }
    }
};

template <size_t I>
struct enc {
    // Block encoding: Go from 0 to (block size - 1), append a symbol for each iteration unconditionally.
    template <typename Codec, typename CodecVariant, typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void block(Result& encoded, ResultState& state, const uint8_t* src)
    {
        using EncodedBlockSizeT = decltype(Codec::encoded_block_size());
        constexpr static const EncodedBlockSizeT SymbolIndex = static_cast<EncodedBlockSizeT>(I - 1);

        enc<I - 1>().template block<Codec, CodecVariant>(encoded, state, src);
        data::put(encoded, state, CodecVariant::symbol(Codec::template index<SymbolIndex>(src)));
    }

    // Tail encoding: Go from 0 until (runtime) num_symbols, append a symbol for each iteration.
    template <typename Codec, typename CodecVariant, typename Result, typename ResultState,
            typename EncodedBlockSizeT = decltype(Codec::encoded_block_size())>
    static CPPCODEC_ALWAYS_INLINE void tail(
            Result& encoded, ResultState& state, const uint8_t* src, EncodedBlockSizeT num_symbols)
    {
        constexpr static const EncodedBlockSizeT SymbolIndex = Codec::encoded_block_size() - I;
        constexpr static const EncodedBlockSizeT NumSymbols = SymbolIndex + static_cast<EncodedBlockSizeT>(1);

        if (num_symbols == NumSymbols) {
            data::put(encoded, state, CodecVariant::symbol(Codec::template index_last<SymbolIndex>(src)));
            padder<CodecVariant::generates_padding()> pad;
#ifdef _MSC_VER
            pad.operator()<CodecVariant>(encoded, state, Codec::encoded_block_size() - NumSymbols);
#else
            pad.template operator()<CodecVariant>(encoded, state, Codec::encoded_block_size() - NumSymbols);
#endif
            return;
        }
        data::put(encoded, state, CodecVariant::symbol(Codec::template index<SymbolIndex>(src)));
        enc<I - 1>().template tail<Codec, CodecVariant>(encoded, state, src, num_symbols);
    }
};

template<> // terminating specialization
struct enc<0> {
    template <typename Codec, typename CodecVariant, typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void block(Result&, ResultState&, const uint8_t*) { }

    template <typename Codec, typename CodecVariant, typename Result, typename ResultState,
            typename EncodedBlockSizeT = decltype(Codec::encoded_block_size())>
    static CPPCODEC_ALWAYS_INLINE void tail(Result&, ResultState&, const uint8_t*, EncodedBlockSizeT)
    {
        abort(); // Not reached: block() should be called if num_symbols == block size, not tail().
    }
};

template <typename Codec, typename CodecVariant>
template <typename Result, typename ResultState>
inline void stream_codec<Codec, CodecVariant>::encode(
        Result& encoded_result, ResultState& state,
        const uint8_t* src, size_t src_size)
{
    using encoder = enc<Codec::encoded_block_size()>;

    const uint8_t* src_end = src + src_size;

    if (src_size >= Codec::binary_block_size()) {
        src_end -= Codec::binary_block_size();

        for (; src <= src_end; src += Codec::binary_block_size()) {
            encoder::template block<Codec, CodecVariant>(encoded_result, state, src);
        }
        src_end += Codec::binary_block_size();
    }

    if (src_end > src) {
        auto remaining_src_len = src_end - src;
        if (!remaining_src_len || remaining_src_len >= Codec::binary_block_size()) {
            abort();
            return;
        }
        auto num_symbols = Codec::num_encoded_tail_symbols(static_cast<uint8_t>(remaining_src_len));
        encoder::template tail<Codec, CodecVariant>(encoded_result, state, src, num_symbols);
    }
}

// Range & lookup table generation, see
// http://stackoverflow.com/questions/13313980/populate-an-array-using-constexpr-at-compile-time
// and http://cplusadd.blogspot.ca/2013/02/c11-compile-time-lookup-tablearray-with.html

template<unsigned... Is> struct seq {};

template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N - 4, N - 4, N - 3, N - 2, N - 1, Is...> {
    // Clang up to 3.6 has a limit of 256 for template recursion,
    // so pass a few more symbols at once to make it work.
    static_assert(N % 4 == 0, "I must be divisible by 4 to eventually end at 0");
};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...> {};

template <size_t N>
struct lookup_table_t {
    alphabet_index_t lookup[N];
    static constexpr size_t size = N;
};

template<typename LambdaType, unsigned... Is>
constexpr lookup_table_t<sizeof...(Is)> make_lookup_table(seq<Is...>, LambdaType value_for_index) {
    return { { value_for_index(Is)... } };
}

template<unsigned N, typename LambdaType>
constexpr lookup_table_t<N> make_lookup_table(LambdaType evalFunc) {
    return make_lookup_table(gen_seq<N>(), evalFunc);
}

// CodecVariant::symbol() provides a symbol for an index.
// Use recursive templates to get the inverse lookup table for fast decoding.

template <typename T>
static CPPCODEC_ALWAYS_INLINE constexpr size_t num_possible_values()
{
    return static_cast<size_t>(
            static_cast<intmax_t>(std::numeric_limits<T>::max())
                    - static_cast<intmax_t>(std::numeric_limits<T>::min()) + 1);
}

template <typename CodecVariant, alphabet_index_t InvalidIdx, size_t I>
struct index_if_in_alphabet {
    static CPPCODEC_ALWAYS_INLINE constexpr alphabet_index_t for_symbol(char symbol)
    {
        return (CodecVariant::symbol(
                    static_cast<alphabet_index_t>(CodecVariant::alphabet_size() - I)) == symbol)
            ? static_cast<alphabet_index_t>(CodecVariant::alphabet_size() - I)
            : index_if_in_alphabet<CodecVariant, InvalidIdx, I - 1>::for_symbol(symbol);
    }
};
template <typename CodecVariant, alphabet_index_t InvalidIdx>
struct index_if_in_alphabet<CodecVariant, InvalidIdx, 0> { // terminating specialization
    static CPPCODEC_ALWAYS_INLINE constexpr alphabet_index_t for_symbol(char)
    {
        return InvalidIdx;
    }
};

template <typename CodecVariant, size_t I>
struct padding_searcher {
    static CPPCODEC_ALWAYS_INLINE constexpr bool exists_padding_symbol()
    {
        // Clang up to 3.6 has a limit of 256 for template recursion,
        // so pass a few more symbols at once to make it work.
        static_assert(I % 4 == 0, "I must be divisible by 4 to eventually end at 0");

        return CodecVariant::is_padding_symbol(
                        static_cast<char>(num_possible_values<char>() - I - 4))
                || CodecVariant::is_padding_symbol(
                        static_cast<char>(num_possible_values<char>() - I - 3))
                || CodecVariant::is_padding_symbol(
                        static_cast<char>(num_possible_values<char>() - I - 2))
                || CodecVariant::is_padding_symbol(
                        static_cast<char>(num_possible_values<char>() - I - 1))
                || padding_searcher<CodecVariant, I - 4>::exists_padding_symbol();
    }
};
template <typename CodecVariant>
struct padding_searcher<CodecVariant, 0> { // terminating specialization
    static CPPCODEC_ALWAYS_INLINE constexpr bool exists_padding_symbol() { return false; }
};

template <typename CodecVariant>
struct alphabet_index_info
{
    static constexpr const size_t num_possible_symbols = num_possible_values<char>();

    static constexpr const alphabet_index_t padding_idx = 1 << 8;
    static constexpr const alphabet_index_t invalid_idx = 1 << 9;
    static constexpr const alphabet_index_t eof_idx = 1 << 10;
    static constexpr const alphabet_index_t stop_character_mask = static_cast<alphabet_index_t>(~0xFFu);

    static constexpr const bool padding_allowed = padding_searcher<
            CodecVariant, num_possible_symbols>::exists_padding_symbol();

    static CPPCODEC_ALWAYS_INLINE constexpr bool allows_padding()
    {
        return padding_allowed;
    }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_padding(alphabet_index_t idx)
    {
        return allows_padding() ? (idx == padding_idx) : false;
    }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_invalid(alphabet_index_t idx) { return idx == invalid_idx; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_eof(alphabet_index_t idx) { return idx == eof_idx; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_stop_character(alphabet_index_t idx)
    {
        return (idx & stop_character_mask) != 0;
    }

private:
    static CPPCODEC_ALWAYS_INLINE constexpr
    alphabet_index_t valid_index_or(alphabet_index_t a, alphabet_index_t b)
    {
        return a == invalid_idx ? b : a;
    }

    using idx_if_in_alphabet = index_if_in_alphabet<
            CodecVariant, invalid_idx, CodecVariant::alphabet_size()>;

    static CPPCODEC_ALWAYS_INLINE constexpr alphabet_index_t index_of(char symbol)
    {
        return valid_index_or(idx_if_in_alphabet::for_symbol(symbol),
            CodecVariant::is_eof_symbol(symbol) ? eof_idx
            : CodecVariant::is_padding_symbol(symbol) ? padding_idx
            : invalid_idx);
    }

    // GCC <= 4.9 has a bug with retaining constexpr when passing a function pointer.
    // To get around this, we'll create a callable with operator() and pass that one.
    // Unfortunately, MSVC prior to VS 2017 (for MinSizeRel or Release builds)
    // chokes on this by compiling the project in 20 minutes instead of seconds.
    // So let's define two separate variants and remove the old GCC one whenever we
    // decide not to support GCC < 5.0 anymore.
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
    struct index_at {
        CPPCODEC_ALWAYS_INLINE constexpr alphabet_index_t operator()(size_t symbol) const {
            return index_of(CodecVariant::normalized_symbol(static_cast<char>(symbol)));
        }
    };
#else
    static CPPCODEC_ALWAYS_INLINE constexpr alphabet_index_t index_at(size_t symbol)
    {
        return index_of(CodecVariant::normalized_symbol(static_cast<char>(symbol)));
    }
#endif

public:
    struct lookup {
        static CPPCODEC_ALWAYS_INLINE alphabet_index_t for_symbol(char symbol)
        {
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
            static constexpr const auto t = make_lookup_table<num_possible_symbols>(index_at());
#else
            static constexpr const auto t = make_lookup_table<num_possible_symbols>(&index_at);
#endif
            static_assert(t.size == num_possible_symbols,
                    "lookup table must cover each possible (character) symbol");
            return t.lookup[static_cast<uint8_t>(symbol)];
        }
    };
};

//
// At long last! The actual decode/encode functions.

template <typename Codec, typename CodecVariant>
template <typename Result, typename ResultState>
inline void stream_codec<Codec, CodecVariant>::decode(
        Result& binary_result, ResultState& state,
        const char* src_encoded, size_t src_size)
{
    using alphabet_index_lookup = typename alphabet_index_info<CodecVariant>::lookup;
    const char* src = src_encoded;
    const char* src_end = src + src_size;

    alphabet_index_t alphabet_indexes[Codec::encoded_block_size()] = {};
    alphabet_indexes[0] = alphabet_index_info<CodecVariant>::eof_idx;

    alphabet_index_t* const alphabet_index_start = &alphabet_indexes[0];
    alphabet_index_t* const alphabet_index_end = &alphabet_indexes[Codec::encoded_block_size()];
    alphabet_index_t* alphabet_index_ptr = &alphabet_indexes[0];

    while (src < src_end) {
        if (CodecVariant::should_ignore(*src)) {
            ++src;
            continue;
        }
        *alphabet_index_ptr = alphabet_index_lookup::for_symbol(*src);
        if (alphabet_index_info<CodecVariant>::is_stop_character(*alphabet_index_ptr)) {
            break;
        }
        ++src;
        ++alphabet_index_ptr;

        if (alphabet_index_ptr == alphabet_index_end) {
            Codec::decode_block(binary_result, state, alphabet_indexes);
            alphabet_index_ptr = alphabet_index_start;
        }
    }

    if (alphabet_index_info<CodecVariant>::is_invalid(*alphabet_index_ptr)) {
        throw symbol_error(*src);
    }
    ++src;

    alphabet_index_t* last_index_ptr = alphabet_index_ptr;
    if (alphabet_index_info<CodecVariant>::is_padding(*last_index_ptr)) {
        if (last_index_ptr == alphabet_index_start) {
            // Don't accept padding at the start of a block.
            // The encoder should have omitted that padding altogether.
            throw padding_error();
        }
        // We're in here because we just read a (first) padding character. Try to read more.
        // Count with last_index_ptr, but store in alphabet_index_ptr so we don't
        // overflow the array in case the input data is too long.
        ++last_index_ptr;
        while (src < src_end) {
            *alphabet_index_ptr = alphabet_index_lookup::for_symbol(*(src++));

            if (alphabet_index_info<CodecVariant>::is_eof(*alphabet_index_ptr)) {
                *alphabet_index_ptr = alphabet_index_info<CodecVariant>::padding_idx;
                break;
            }
            if (!alphabet_index_info<CodecVariant>::is_padding(*alphabet_index_ptr)) {
                throw padding_error();
            }

            ++last_index_ptr;
            if (last_index_ptr > alphabet_index_end) {
                throw padding_error();
            }
        }
    }

    if (last_index_ptr != alphabet_index_start)  {
        if ((CodecVariant::requires_padding()
                    || alphabet_index_info<CodecVariant>::is_padding(*alphabet_index_ptr)
                    ) && last_index_ptr != alphabet_index_end)
        {
            // If the input is not a multiple of the block size then the input is incorrect.
            throw padding_error();
        }
        if (alphabet_index_ptr >= alphabet_index_end) {
            abort();
            return;
        }
        Codec::decode_tail(binary_result, state, alphabet_indexes,
                static_cast<size_t>(alphabet_index_ptr - alphabet_index_start));
    }
}

template <typename Codec, typename CodecVariant>
inline constexpr size_t stream_codec<Codec, CodecVariant>::encoded_size(size_t binary_size) noexcept
{
    using C = Codec;

    // constexpr rules make this a lot harder to read than it actually is.
    return CodecVariant::generates_padding()
            // With padding, the encoded size is a multiple of the encoded block size.
            // To calculate that, round the binary size up to multiple of the binary block size,
            // then convert to encoded by multiplying with { base32: 8/5, base64: 4/3 }.
            ? (binary_size + (C::binary_block_size() - 1)
                    - ((binary_size + (C::binary_block_size() - 1)) % C::binary_block_size()))
                    * C::encoded_block_size() / C::binary_block_size()
            // No padding: only pad to the next multiple of 5 bits, i.e. at most a single extra byte.
            : (binary_size * C::encoded_block_size() / C::binary_block_size())
                    + (((binary_size * C::encoded_block_size()) % C::binary_block_size()) ? 1 : 0);
}

template <typename Codec, typename CodecVariant>
inline constexpr size_t stream_codec<Codec, CodecVariant>::decoded_max_size(size_t encoded_size) noexcept
{
    using C = Codec;

    return CodecVariant::requires_padding()
            ? (encoded_size / C::encoded_block_size() * C::binary_block_size())
            : (encoded_size / C::encoded_block_size() * C::binary_block_size())
                    + ((encoded_size % C::encoded_block_size())
                            * C::binary_block_size() / C::encoded_block_size());
}

} // namespace detail
} // namespace cppcodec

#endif // CPPCODEC_DETAIL_STREAM_CODEC

A include/cppcodec/hex_default_lower.hpp => include/cppcodec/hex_default_lower.hpp +31 -0
@@ 0,0 1,31 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_HEX_DEFAULT_LOWER
#define CPPCODEC_HEX_DEFAULT_LOWER

#include "hex_lower.hpp"

using hex = cppcodec::hex_lower;

#endif // CPPCODEC_HEX_DEFAULT_LOWER

A include/cppcodec/hex_default_upper.hpp => include/cppcodec/hex_default_upper.hpp +31 -0
@@ 0,0 1,31 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_HEX_DEFAULT_UPPER
#define CPPCODEC_HEX_DEFAULT_UPPER

#include "hex_upper.hpp"

using hex = cppcodec::hex_upper;

#endif // CPPCODEC_HEX_DEFAULT_UPPER

A include/cppcodec/hex_lower.hpp => include/cppcodec/hex_lower.hpp +75 -0
@@ 0,0 1,75 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_HEX_LOWER
#define CPPCODEC_HEX_LOWER

#include "detail/codec.hpp"
#include "detail/hex.hpp"

namespace cppcodec {

namespace detail {

static constexpr const char hex_lower_alphabet[] = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // at index 10
        'a', 'b', 'c', 'd', 'e', 'f'
};

class hex_lower
{
public:
    template <typename Codec> using codec_impl = stream_codec<Codec, hex_lower>;

    static CPPCODEC_ALWAYS_INLINE constexpr size_t alphabet_size() {
        static_assert(sizeof(hex_lower_alphabet) == 16, "hex alphabet must have 16 values");
        return sizeof(hex_lower_alphabet);
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char symbol(alphabet_index_t index)
    {
        return hex_lower_alphabet[index];
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char normalized_symbol(char c)
    {
        // Hex decoding is always case-insensitive (even in RFC 4648), the question
        // is only for encoding whether to use upper-case or lower-case letters.
        return (c >= 'A' && c <= 'F') ? (c - 'A' + 'a') : c;
    }

    static CPPCODEC_ALWAYS_INLINE constexpr bool generates_padding() { return false; }
    // FIXME: doesn't require padding, but requires a multiple of the encoded block size (2)
    static CPPCODEC_ALWAYS_INLINE constexpr bool requires_padding() { return false; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_padding_symbol(char) { return false; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_eof_symbol(char c) { return c == '\0'; }

    // Sometimes hex strings include whitespace, but this variant forbids it.
    static CPPCODEC_ALWAYS_INLINE constexpr bool should_ignore(char) { return false; }
};

} // namespace detail

using hex_lower = detail::codec<detail::hex<detail::hex_lower>>;

} // namespace cppcodec

#endif // CPPCODEC_HEX_LOWER

A include/cppcodec/hex_upper.hpp => include/cppcodec/hex_upper.hpp +75 -0
@@ 0,0 1,75 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_HEX_UPPER
#define CPPCODEC_HEX_UPPER

#include "detail/codec.hpp"
#include "detail/hex.hpp"

namespace cppcodec {

namespace detail {

static constexpr const char hex_upper_alphabet[] = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        'A', 'B', 'C', 'D', 'E', 'F'
};

class hex_upper
{
public:
    template <typename Codec> using codec_impl = stream_codec<Codec, hex_upper>;

    static CPPCODEC_ALWAYS_INLINE constexpr size_t alphabet_size() {
        static_assert(sizeof(hex_upper_alphabet) == 16, "hex alphabet must have 16 values");
        return sizeof(hex_upper_alphabet);
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char symbol(alphabet_index_t index)
    {
        return hex_upper_alphabet[index];
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char normalized_symbol(char c)
    {
        // Hex decoding is always case-insensitive (even in RFC 4648), the question
        // is only for encoding whether to use upper-case or lower-case letters.
        return (c >= 'a' && c <= 'f') ? (c - 'a' + 'A') : c;
    }

    static CPPCODEC_ALWAYS_INLINE constexpr bool generates_padding() { return false; }
    // FIXME: doesn't require padding, but requires a multiple of the encoded block size (2)
    static CPPCODEC_ALWAYS_INLINE constexpr bool requires_padding() { return false; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_padding_symbol(char) { return false; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_eof_symbol(char c) { return c == '\0'; }

    // Sometimes hex strings include whitespace, but this variant forbids it.
    static CPPCODEC_ALWAYS_INLINE constexpr bool should_ignore(char) { return false; }
};

} // namespace detail

using hex_upper = detail::codec<detail::hex<detail::hex_upper>>;

} // namespace cppcodec

#endif // CPPCODEC_HEX_UPPER

A include/cppcodec/parse_error.hpp => include/cppcodec/parse_error.hpp +109 -0
@@ 0,0 1,109 @@
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  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.
 */

#ifndef CPPCODEC_PARSE_ERROR
#define CPPCODEC_PARSE_ERROR

#include <stdexcept>
#include <string>

namespace cppcodec {

namespace detail {
// <*stream> headers include a lot of code and noticeably increase compile times.
// The only thing we want from them really is a char-to-string conversion.
// That's easy to implement with many less lines of code, so let's do it ourselves.
template <int N>
static void uctoa(unsigned char n, char (&s)[N])
{
    static_assert(N >= 4, "need at least 4 bytes to convert an unsigned char to string safely");
    int i = sizeof(s) - 1;
    int num_chars = 1;
    s[i--] = '\0';
    do { // generate digits in reverse order
        s[i--] = n % 10 + '0'; // get next digit
        ++num_chars;
    } while ((n /= 10) > 0); // delete it

    if (num_chars == sizeof(s)) {
        return;
    }
    for (i = 0; i < num_chars; ++i) { // move chars to front of string
        s[i] = s[i + (sizeof(s) - num_chars)];
    }
}
} // end namespace detail


class parse_error : public std::domain_error
{
public:
    using std::domain_error::domain_error;
};

// Avoids memory allocation, so it can be used in constexpr functions.
class symbol_error : public parse_error
{
public:
    symbol_error(char c)
        : parse_error(symbol_error::make_error_message(c))
        , m_symbol(c)
    {
    }

    symbol_error(const symbol_error&) = default;

    char symbol() const noexcept { return m_symbol; }

private:
    static std::string make_error_message(char c)
    {
        char s[4];
        detail::uctoa(*reinterpret_cast<unsigned char*>(&c), s);
        return std::string("parse error: character [") + &(s[0]) + " '" + c + "'] out of bounds";
    }

private:
    char m_symbol;
};

class invalid_input_length : public parse_error
{
public:
    using parse_error::parse_error;
};

class padding_error : public invalid_input_length
{
public:
    padding_error()
        : invalid_input_length("parse error: codec expects padded input string but padding was invalid")
    {
    }

    padding_error(const padding_error&) = default;
};

} // namespace cppcodec

#endif // CPPCODEC_PARSE_ERROR

M include/main.hpp => include/main.hpp +36 -16
@@ 12,8 12,8 @@
struct Params {
  std::string salt;
  uint64_t cost;
  uint8_t block;
  uint64_t parallel;
  uint32_t block;
  uint32_t parallel;
  bool s_option_passed;
  bool nrp_option_passed;
  const std::string VERSION;


@@ 22,16 22,18 @@ struct Params {

/** --- Non-Member Helper Functions --- **/


inline void print_version(const std::string &version) {
  std::cout << "scry version: " << version << '\n';
}


bool check_for_ascii(std::string val) {
  bool rv = true;
  for (auto& c : val) {
    int range = 126 - char(c);
    if (range < 0 && range > 94) {
      // non 7-bit ascii detected
    if (range < 0 || range > 94) {
      std::cout << "Bad salt given. Contains non-7bit ascii characters.\n";
      rv = false;
      break;
    } 


@@ 39,33 41,51 @@ bool check_for_ascii(std::string val) {
  return rv;
}

/* r, p, and buflen
 * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32.
 * N must be a power of 2 greater than 1 and less than 2^(128 * r / 8).
 */

bool check_args(CLI::App& app, Params& p) {
  if (app.count("-v")) {
    print_version(p.VERSION);
    return false;
  }
  if (app.count("-s")) {
    if(!check_for_ascii(p.salt)) return false;
    // perform our checks on the given salt
    if (!check_for_ascii(p.salt)) {
      return false;
    }
    p.s_option_passed = true;
  }
  if (app.count("-r")) {
    // check range
    // must satisfy r * p < 2^30
    if (!(p.block * p.parallel < pow(2,30))) {
      std::cout << "r value was too big or small!\n";
      return false;
    }
    p.nrp_option_passed = true;
    return true;
  }
  if (app.count("-p")) {
    // The parallelization parameter p
    // is a positive integer less than or equal
    // to ((2^32-1) * 32) / (128 * r)
    if(!(p.parallel <= ((2^32-1) * 32 / (128 * p.block)))) return false;
    // to ((2^32 - 1) * 32) / (128 * r)
    if (!(p.parallel <= ((pow(2,32) - 1) * 32 / (128 * p.block)))) {
      std::cout << "p value was too big or small!\n";
      return false;
    }
    p.nrp_option_passed = true;
  }
  if (app.count("-n")) {
    const uint64_t cost = p.cost;
    const auto nexp = ((128 * p.block) / 8);
    const auto max_n = pow(2,nexp);
    if (cost <= 1 || cost >= max_n) {
      std::cout << "n value was too big or small!\n";
      return false;
    }
    // is the n value a power of 2?
    if ((cost > 0) && (cost & (cost - 1))) {
      std::cout << "n value was not a power of 2 (e.g. 1024, 2048, 4096, etc).\n";
      return false;
    }
    p.nrp_option_passed = true;
    return true;
  }
 
  return true;
}


M include/scry.hpp => include/scry.hpp +4 -3
@@ 13,12 13,13 @@
#include <cstring>
#include "CLI11.hpp"
#include "libscrypt.h"

#include <cppcodec/base64_rfc4648.hpp>

class Scry {
public:
  int make_key(const char* password, uint64_t n, uint8_t r, uint64_t p) const;
  int make_key(const char* salt, const char* password, uint64_t n, uint8_t r, uint64_t p) const;
  int make_key(const char* password, uint64_t n, uint32_t r, uint32_t p) const;
  int make_key(std::string salt, std::string password, uint64_t n, uint32_t r, uint32_t p) const;
  int make_key(uint8_t* salt, size_t saltlen, std::string password, uint64_t n, uint32_t r, uint32_t p) const;
};

#endif // MY_SCRY

M main.cpp => main.cpp +10 -7
@@ 9,7 9,7 @@

int main(int argc, char* argv[]) {
  const std::string VERSION = "0.1";
  Params p{ "", 4096, 8, 2, false, false, VERSION };
  Params p{ "", 8192, 8, 2, false, false, VERSION };
  int rv = 1;
  
  // parse the user input flags & options


@@ 17,7 17,7 @@ int main(int argc, char* argv[]) {
  app.ignore_case();

  app.add_option("-s, --salt", p.salt, "Specify your own salt (default behavior uses /dev/urandom)");
  app.add_option("-n, --cpucost", p.cost, "The CPU/Memory cost (default 4096)");
  app.add_option("-n, --cpucost", p.cost, "The CPU/Memory cost (default 8192)");
  app.add_option("-r, --blocksize", p.block, "Change the standard blocksize (default 8)");
  app.add_option("-p, --parallelism", p.parallel, "How much parallelism to use (default 2)");
  app.add_flag("-v,--version", "Display the version of scry");


@@ 39,15 39,18 @@ int main(int argc, char* argv[]) {
    while (std::getline(std::cin, line) && !line.empty()) {
      str_pass = line;
    }
    bool pass_ok = str_pass.length() < 513;
    bool pass_ok = (str_pass.length() < 513) && (str_pass.length() > 0);
    if (p.s_option_passed && pass_ok) {
      // call other function.
      const char* password = line.c_str();
      //rv = scry_obj->make_key(p.salt, password, p.cost, p.block, p.parallel);
      // call function using the salt provided by the user.
      rv = scry_obj->make_key(p.salt, str_pass, p.cost, p.block, p.parallel);
    }
    else if (!p.s_option_passed && p.nrp_option_passed) {
      // salt was not provided, but the -n, -r, or -p flag was used
      // so we have to grab a random salt from /dev/urandom
      // so we have to generate one..
      size_t saltlen = 16;
      uint8_t created_salt[16];
      libscrypt_salt_gen(created_salt, saltlen);
      rv = scry_obj->make_key(created_salt, saltlen, str_pass, p.cost, p.block, p.parallel);
    }
    else if (pass_ok) {
      const char* password = line.c_str();

M scry.1 => scry.1 +15 -6
@@ 3,20 3,29 @@
scry \- Generate a key on the CLI using the scrypt KDF
.SH SYNOPSIS
.B scry
[\fB-s mysalt\fR]
[\fB-v flagB\fR]
[\fB-h flagC\fR]
[\fB-s,--salt mysalt\fR]
[\fB-n,--cpucost cost\fR]
[\fB-r,--blocksize size\fR]
[\fB-p,--parallel cost\fR]
[\fB-v,--version\fR]
[\fB-h,--help flagC\fR]
.IR ...
.SH DESCRIPTION
.B scry
generate strong keys for encryption from the command line using scrypt (a memory hard key derivation function)
generate strong keys for encryption from the command line using scrypt (a memory hard key derivation function). Output is in the Modular Crypt Format.
.SH OPTIONS
.TP
.BR -s " " \fImysalt\fR
A unique string to be used as a salt (aka nonce).
.TP
.BR -b " " \fIoption1 | option2 | option3\fR
Describe what flag -b does.
.BR -n " " --cpucost " " \fIcost\fR
The CPU and RAM cost. (default 8192)
.TP
.BR -r " " --block \fIblocksize\fR
The block size to use. (default 8)
.TP
.BR -p " " --parallel " " \fIparallelism\fR
A large value of p can be used to increase the computational cost of Scrypt. (default 2)
.TP
.BR -h --help" "
Shows the help information.

M scry.cpp => scry.cpp +50 -5
@@ 12,8 12,8 @@
/* --- Member Functions --- */


int Scry::make_key(const char* password, uint64_t n, uint8_t r, uint64_t p) const {
  char dest[125];
int Scry::make_key(const char* password, uint64_t n, uint32_t r, uint32_t p) const {
  char dest[129];
  try {
    libscrypt_hash(&dest[0], password, n, r, p);
  }


@@ 25,7 25,52 @@ int Scry::make_key(const char* password, uint64_t n, uint8_t r, uint64_t p) cons
  return 1;
}

int Scry::make_key(const char* salt, const char* password, uint64_t n, uint8_t r, uint64_t p) const {
  int i = 0;
  return i;

int Scry::make_key(std::string salt, std::string password, uint64_t n, uint32_t r, uint32_t p) const {
  uint8_t dest[64];
  const char* ccsalt = salt.c_str();
  const char* ccpass = password.c_str();
  size_t saltlen = strlen(salt.c_str());
  size_t passlen = strlen(password.c_str());
  const uint8_t* upass = reinterpret_cast<const uint8_t*>(ccpass);
  const uint8_t* usalt = reinterpret_cast<const uint8_t*>(ccsalt);
  char mcf[128];
  try {
    libscrypt_scrypt(upass, passlen, usalt, saltlen, n, r, p, dest, sizeof(dest));
    std::string encoded_dest = cppcodec::base64_rfc4648::encode(dest, sizeof(dest));
    const char* cc_encoded_dest = encoded_dest.c_str();
    libscrypt_mcf(n, r, p, ccsalt, cc_encoded_dest, &mcf[0]);
  }
  catch(...) {
    std::cout << "Caught exception during key creation!\n";
    return 0;
  }
  std::cout << "salt + key = " << mcf << "\n";
  return 1;
}


// This version is called when we were given n, r, or p but not a salt..
int Scry::make_key(uint8_t* salt, size_t saltlen, std::string password, uint64_t n, uint32_t r, uint32_t p) const {
  uint8_t dest[64];
  const char* ccpass = password.c_str();
  size_t passlen = strlen(password.c_str());
  const uint8_t* upass = reinterpret_cast<const uint8_t*>(ccpass);
  char mcf[128];
  try {
    std::string encoded_salt = cppcodec::base64_rfc4648::encode(salt, saltlen);
    const char* cc_encoded_salt = encoded_salt.c_str();
    size_t cce_saltlen = sizeof(cc_encoded_salt)/sizeof(cc_encoded_salt[0]);
    const uint8_t* usalt = reinterpret_cast<const uint8_t*>(cc_encoded_salt);
    libscrypt_scrypt(upass, passlen, usalt, cce_saltlen, n, r, p, dest, sizeof(dest));
    std::string encoded_dest = cppcodec::base64_rfc4648::encode(dest, sizeof(dest));
    const char* cc_encoded_dest = encoded_dest.c_str();
    libscrypt_mcf(n, r, p, cc_encoded_salt, cc_encoded_dest, &mcf[0]);
  }
  catch(...) {
    std::cout << "Caught exception during key creation!\n";
    return 0;
  }
  std::cout << "salt + key = " << mcf << "\n";
  return 1;
}