ArrayEncoder

From Crypto++ Wiki
Jump to navigation Jump to search

ArrayEncoder is a simple example of a FilterWithBufferedInput derived class. You can use it to quickly generate C style arrays and strings using a HexEncoder. The outputted strings will not exceed a chosen width.

The ArrayEncoder takes a pointer to a BufferedTransformation. Because a pointer is taken, the ArrayEncoder owns the attached transformation, and therefore will destroy it. See ownership for more details.

The ArrayEncoder class appeared around Crypto++ 5.6.4 to help generate self tests. There is no ArrayDecoder class.

ArrayEncoder is not part of the Crypto++ library. If you want it, then paste it into a file like hex.h below the HexEncoder and HexDecoder class definitions. ArrayEncoder is a header-only definition so you don't need to modify source files. If you want to add it to the library, be sure its in the CryptoPP namespace.

Construction

ArrayEncoder(BufferedTransformation *attachment=NULL,
             bool uppercase=true,
             unsigned int width=80,
             bool asString=true)

attachment is a BufferedTransformation, such as another filter or sink. If attachment is NULL, then the ArrayEncoder object will internally accumulate the output byte stream.

uppercase is an output formatting option and determines if output is uppercase or lowercase.

width is the maximum length of an output line. Lines may be shorter, but they will not be longer.

asString is a flag indicating C-style strings. If asString is true then encoded bytes are prefixed with \x and each line is quoted. Otherwise, encoded bytes are prefixed with 0x and separated with commas.

Usage

SecByteBlock scratch(34);
OS_GenerateRandomBlock(false, scratch, scratch.size());
ArraySource as(scratch, scratch.size(), true, new ArrayEncoder(new FileSink(cout)));

The result is as shown blow. You will have to add the opening and closing braces.

0x2A,0x5A,0x7D,0xC4,0x15,0x7F,0x78,0x2D,0xE4,0x65,0xDD,0x2F,0x2C,0xA8,0x01,0x5B,
0xA2,0x92,0xA1,0xCF,0x33,0x11,0xFF,0x12,0xAC,0x49,0x3D,0x1C,0x52,0x48,0xDC,0x72,
0xB0,0xF2

If you want a C style string, then set asString to true:

SecByteBlock scratch(34);
OS_GenerateRandomBlock(false, scratch, scratch.size());
ArraySource as(scratch, scratch.size(), true, new ArrayEncoder(new FileSink(cout), true, 80, true));

It produces the result:

"\xCF\x75\xA2\x27\xD6\xD0\x1B\x2E\x9F\x44\x3C\x1C\x8B\x5C\x22\x62\x66\xA1\x23"
"\x81\x6D\x15\x4F\xB4\x35\xDA\x01\xF2\xF8\x71\xAF\xFF\x86\xAA"

ArrayEncoder

class ArrayEncoder : public FilterWithBufferedInput
{
public:
    ArrayEncoder(BufferedTransformation *attachment=NULL, bool uppercase=true, unsigned int width=80, bool asString=true)
        : FilterWithBufferedInput(0, 1, 0, attachment), m_encoder(), m_width(width), m_firstPut(false), m_asString(asString)
    {
        AlgorithmParameters params;
        if (m_asString)
        {
            params = MakeParameters
                (Name::Uppercase(), uppercase)
                (Name::GroupSize(), 2)
                (Name::Terminator(), ConstByteArrayParameter(""))
                (Name::Separator(), ConstByteArrayParameter("\\x"));
        }
        else
        {
            params = MakeParameters
                (Name::Uppercase(), uppercase)
                (Name::GroupSize(), 2)
                (Name::Terminator(), ConstByteArrayParameter(""))
                (Name::Separator(), ConstByteArrayParameter(",0x"));
        }

        m_encoder.IsolatedInitialize(params);

        if (m_width < 40)
            m_width = 40;
    }

    virtual void FirstPut(const byte *inString)
    {
        // m_firstSize is from base class
        ArrayEncoder::Put2(inString, m_firstSize, 0, true);
    }

    virtual void LastPut(const byte *inString, size_t length)
    {
        ArrayEncoder::Put2(inString, length, -1, true);
    }

    virtual size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking)
    {
        // Allow member filter to process data and messageEnd
        if (inString && length)
            m_encoder.Put2(inString, length, messageEnd, blocking);

        // 8*m_width buffers for the output device. Push data if messageEnd
        if (m_encoder.MaxRetrievable() > 8*m_width || messageEnd)
            TransferEncoded(!!messageEnd);

        // Pass a messageEnd to the attached transformation
        if (messageEnd)
            AttachedTransformation()->MessageEnd();

        return 0;
    }

protected:

    void TransferEncodedLine(const SecByteBlock& str)
    {
        if (m_asString)    // Opening quote
            AttachedTransformation()->Put((const byte*)"\"", 1);

        AttachedTransformation()->Put(str.data(), str.size());

        if (m_asString)    // Closing quote
            AttachedTransformation()->Put((const byte*)"\"", 1);

        AttachedTransformation()->Put((const byte*)"\n", 1);
    }

    void TransferEncoded(bool last=false)
    {
        if (last)
            m_encoder.MessageEnd();

        if (!m_encoder.AnyRetrievable())
            return;

        // m_asString and -2 due to quotes
        const size_t size = (m_asString ? ((m_width/4)*4-2): (m_width/5)*5);
        SecByteBlock scratch(size);

        /////////////////////////////////////////////////////////////////////////

        // First Put (maybe last, too)
        if (!m_firstPut)
        {
            // First put needs the lead 0x or \x
            scratch[1] = 'x';
            if (m_asString)
                scratch[0] = '\\';
            else
                scratch[0] = '0';

            size_t rem = m_encoder.Get(scratch.data()+2, size-2);
            scratch.resize(rem+2);
            TransferEncodedLine(scratch);

            m_firstPut = true;
        }

        // Middle Puts
        while (m_encoder.MaxRetrievable() >= m_width)
        {
            m_encoder.Get(scratch.data(), size);
            TransferEncodedLine(scratch);
        }

        // Last Put
        if (last)
        {
            if (m_encoder.AnyRetrievable())
            {
                size_t rem = m_encoder.Get(scratch.data(), size);
                scratch.resize(rem);
                TransferEncodedLine(scratch);
            }

            m_firstPut = false;
        }
    }

private:

    HexEncoder m_encoder;
    unsigned int m_width;
    bool m_firstPut, m_asString;
};