// bdlde_quotedprintableencoder.h                                     -*-C++-*-
#ifndef INCLUDED_BDLDE_QUOTEDPRINTABLEENCODER
#define INCLUDED_BDLDE_QUOTEDPRINTABLEENCODER

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide automata converting to and from Quoted-Printable encodings.
//
//@CLASSES:
//  bdlde::QuotedPrintableEncoder: automaton for Quoted-Printable encoding
//
//@SEE_ALSO: bdlde_quotedprintabledecoder
//
//@DESCRIPTION: This component provides a class that can be used to encode byte
// sequences of arbitrary length into the Quoted Printable representation
// described in Section 6.7 "Quoted-Printable Content Transfer Encoding" of RFC
// 2045, "Multipurpose Internet Mail Extensions (MIME) Part One: Format of
// Internet Message Bodies."
//
// Each instance of the encoder retains the state of the conversion from one
// supplied input to the next, enabling the processing of segmented input --
// i.e., processing resumes where it left off with the next invocation on new
// input.  Instance methods are provided for the encoder to (1) assert the end
// of input, (2) determine whether the input so far is currently acceptable,
// and (3) indicate whether a non-recoverable error has occurred.
//
///Quoted-Printable Encoding
///-------------------------
// This encoding scheme is suitable for encoding arbitrary data consisting
// primarily of printable text characters.  Additionally, this scheme seeks to
// preserve the integrity of the byte stream during transfer by making it
// difficult for any intermediate interpreting software in the path of the
// transfer to disruptively change its content (e.g., because of trailing
// whitespace and line breaks).  For binary data, Base64 encoding may be a more
// appropriate scheme (see `bdlde_base64`).
//
// The data stream is processed one byte at a time from left to right as
// follows:
//
///General 8-Bit Representation
/// - - - - - - - - - - - - - -
// Any 8-bit input character, except a CR or LF, *may* be represented by an "="
// followed by a 2-digit hexadecimal representation of its ASCII value.  Only
// uppercase hexadecimal digits are allowed.  For example, the letter `n` can
// be encoded into `=6E`.
//
///Literal Representation
/// - - - - - - - - - - -
// Characters with decimal values in the range [33..126], with the exception of
// 61 (`=`), *may* be represented literally as they appear before encoding.
// Hence, in addition to [0-9][a-z][A-Z], the following characters may
// propagate to the encoded stream unchanged.
// ```
// [!"#$%&'()*+,-./:;<>?@[\]^_`{|}~]
// ```
//
///Whitespace
/// - - - - -
// Space and tab *may* be represented literally, unless they appear at the end
// of an encoded line, in which case they must be followed by a `=` character
// serving as a soft line break (see rule #5), or they must be encoded
// according to rule #1.  It follows that any trailing whitespace encountered
// in a Quoted-Printable body must necessarily be added by intermediate
// transport agents and must be deleted during decoding.
//
///Line Breaks
///- - - - - -
// A line break must be represented in the Quoted-Printable encoding as in rule
// number 1, i.e., LF -> =0A; CR -> =0D.
//
///Soft Line Breaks
/// - - - - - - - -
// Encoded lines are required to be no longer than 76 characters in this
// encoding scheme.  Soft line breaks in the form of an `=` sign placed at the
// end of an encoded line are used to break up longer lines, either necessarily
// when the number of encoded characters, including any `=` characters but not
// counting the trailing CRLF, reaches the limit of 76, or at the user's
// discretion -- e.g., during manual encoding.  Soft line breaks are to be
// removed during decoding as they are not part of the original content.
//
// The Quoted-Printable encoding scheme allows one or two forms of encoding
// depending on the value of the character to be encoded as well as its
// location with respect to the end of line.  When both forms are permissible,
// the choice is discretionary.  For example, the word `From` is often used as
// a message separator in the standard UNIX mail folder format.  To reduce the
// chance of a message getting broken, a sentence such as "From point A ..." is
// often best encoded as "=46rom point A ...", although "From point A ..." is
// also a valid encoding.
//
// This implementation by default prefers literal encoding to Quoted-Printable
// encoding.  In the case of a space or tab character happening at the end of
// an encoded line, if there is more input to follow, a soft line break is
// inserted; otherwise, the last line of encoding should be terminated with the
// Quoted Printable encoding of space or tab (a line break is both redundant
// and contrived).
//
// In situations where it is desirable to specify certain characters to be
// encoded to their numeric form, the encoder in this implementation also
// offers a means to specify these characters through the first parameter to
// the following constructor
// ```
//  bdlde::QuotedPrintableEncoder(
//     const char *extraCharsToEncode,
//     bdlde::QuotedPrintableEncoder::LineBreakMode lineBreakMode
//                       = bdlde::QuotedPrintableEncoder::e_CRLF_MODE,
//     int maxLineLength =
//                      bdlde::QuotedPrintableEncoder::k_DEFAULT_MAX_LINELEN);
// ```
//
// The following examples demonstrate the above rules per the design choices
// made for this implementation.  Note that there is a hard line break at the
// 77th character position, immediately after "dozing".
//
///Example 1
///- - - - -
// Data:
// ```
// From point A to point B, the distance is 1245.56 miles.  Driving at a dozing
// speed of 15mph, it will take 2 hours to complete the trip.
// ```
//
// Encoding:
// ```
// =46rom point A to point B, the distance is 1245.56 miles.  Driving at a doz=
// ing=0D=0A speed of 15mph, =
// it will take 2 hours=
// to complete the trip.
// ```
//
///Example 2
///- - - - -
// Data:
// ```
// Hello, world.
// ```
// (The last line of input ends with a whitespace.)
//
// Encoding:
// ```
// Hello, world.=20
// ```
// (In this case, a Quoted Printable is preferred to soft line break as there
// should only be one encoded line.)
//
// The above encoding is acceptable, although it is by no means unique.
//
///Quoted-Printable Decoding
///-------------------------
// (In the following, all rules mentioned refer to those listed in the encoder
// section above.)
//
// The decoding process for this encoding scheme involves:
//
// 1. transforming any encoded character triplets back into their original
//    representation (rule #1 and rule #4).
// 2. literally writing out characters that have not been changed (rule #2).
// 3. deleting any trailing whitespace at the end of an encoded line (rule #3).
// 4. removing the soft line breaks including the `=` prefix (i.e.,
//    concatenating broken sentences) (rule #5).
//
// The standard imposes a maximum of 76 characters exclusive of CRLF; however,
// the decoder implemented in this component will handle lines of arbitrary
// length.
//
// The decoder also provides support for two error-reporting modes,
// configurable at construction: the strict mode and the relaxed mode.  A
// strict-mode decoder stops decoding at the first offending character
// encountered, while a relaxed-mode decoder continues decoding to the end of
// the input, allowing straight pass-through of character sets that cannot be
// interpreted.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Encoding
///- - - - - - - - - -
// The following example shows how to use a `bdlde::QuotedPrintableEncoder`
// object to implement a function, `streamconverter`, that reads text from a
// `bsl::istream`, encodes that text in Quoted-Printable representation, and
// write928s the encoded text to a `bsl::ostream`.  `streamconverter` returns 0
// on success, and a negative value if the input data could not be successfully
// encoded or if there is an I/O error.
// ```
// streamconverter.h                    -*-C++-*-
//
// /// Read the entire contents of the specified input stream `is`, convert
// /// the input plain text to quoted-printable encoding, and write the
// /// encoded text to the specified output stream `os`.  Return 0 on
// /// success, and a negative value otherwise.
// int streamconverter(bsl::ostream& os, bsl::istream& is);
// ```
// We will use fixed-sized input and output buffers in the implementation, but,
// because of the flexibility of `bsl::istream` and the output-buffer
// monitoring functionality of `QuotedPrintableEncoder`, the fixed buffer sizes
// do *not* limit the quantity of data that can be read, encoded, or written to
// the output stream.  The implementation file is as follows.
// ```
// streamconverter.cpp                  -*-C++-*-
//
// #include <streamconverter.h>
//
// #include <bdlde_quotedprintableencoder.h>
//
// namespace BloombergLP {
//
// int streamconverter(bsl::ostream& os, bsl::istream& is)
// {
//     enum {
//         SUCCESS      =  0,
//         ENCODE_ERROR = -1,
//         IO_ERROR     = -2
//     };
// ```
// We declare a `bdlde::QuotedPrintableEncoder` object `converter`, which will
// encode the input data.  Note that various internal buffers and cursors are
// used as needed without further comment.  We read as much data as is
// available from the user-supplied input stream `is` *or* as much as will fit
// in `inputBuffer` before beginning conversion.
// ```
//    bdlde::QuotedPrintableEncoder converter;
//
//    const int INBUFFER_SIZE  = 1 << 10;
//    const int OUTBUFFER_SIZE = 1 << 10;
//
//    char inputBuffer[INBUFFER_SIZE];
//    char outputBuffer[OUTBUFFER_SIZE];
//
//    char *output    = outputBuffer;
//    char *outputEnd = outputBuffer + sizeof outputBuffer;
//
//    while (is.good()) {  // input stream not exhausted
//
//        is.read(inputBuffer, sizeof inputBuffer);
// ```
// With `inputBuffer` now populated, we'll use `converter` in an inner `while`
// loop to encode the input and write the encoded data to `outputBuffer` (via
// the `output` cursor').  Note that if the call to `converter.convert` fails,
// our function terminates with a negative status.
// ```
//        const char *input    = inputBuffer;
//        const char *inputEnd = input + is.gcount();
//
//        while (input < inputEnd) { // input encoding not complete
//
//            int numOut;
//            int numIn;
//
//            int status = converter.convert(output, &numOut, &numIn,
//                                           input,   inputEnd,
//                                           outputEnd - output);
//            if (status < 0) {
//                return ENCODE_ERROR;                               // RETURN
//            }
// ```
// If the call to `converter.convert` returns successfully, we'll see if the
// output buffer is full, and if so, write its contents to the user-supplied
// output stream `os`.  Note how we use the values of `numOut` and `numIn`
// generated by `convert` to update the relevant cursors.
// ```
//            output += numOut;
//            input  += numIn;
//
//            if (output == outputEnd) {  // output buffer full; write data
//                os.write (outputBuffer, sizeof outputBuffer);
//                if (os.fail()) {
//                    return IO_ERROR;                               // RETURN
//                }
//                output = outputBuffer;
//            }
//        }
//    }
// ```
// We have now exited both the input and the "encode" loops.  `converter` may
// still hold encoded output characters, and so we call `converter.endConvert`
// to emit any retained output.  To guarantee correct behavior, we call this
// method in an infinite loop, because it is possible that the retained output
// can fill the output buffer.  In that case, we solve the problem by writing
// the contents of the output buffer to `os` within the loop.  The most likely
// case, however, is that `endConvert` will return 0, in which case we exit the
// loop and write any data remaining in `outputBuffer` to `os`.  As above, if
// `endConvert` fails, we exit the function with a negative return status.
// ```
//    for (;;) {
//
//        int more =
//                  converter.endConvert(output, &numOut, outputEnd - output);
//        if (more < 0) {
//            return ENCODE_ERROR;                                   // RETURN
//        }
//
//        output += numOut;
//
//        if (!more) { // no more output
//            break;
//        }
//
//        assert (output == outputEnd);  // output buffer is full
//
//        os.write (outputBuffer, sizeof outputBuffer);  // write buffer
//        if (os.fail()) {
//            return IO_ERROR;                                       // RETURN
//        }
//        output = outputBuffer;
//    }
//
//    if (output > outputBuffer) { // still data in output buffer; write it
//                                 // all
//        os.write(outputBuffer, output - outputBuffer);
//    }
//
//    return is.eof() && os.good() ? SUCCESS : IO_ERROR;
// }
//
// } // Close namespace BloombergLP
// ```
// For ease of reading, we repeat the full content of the `streamconverter.cpp`
// file without interruption.
// ```
// streamconverter.cpp                  -*-C++-*-
//
// #include <streamconverter.h>
//
// #include <bdlde_quotedprintableencoder.h>
//
// namespace BloombergLP {
//
// int streamconverter(bsl::ostream& os, bsl::istream& is)
// {
//     enum {
//         SUCCESS      =  0,
//         ENCODE_ERROR = -1,
//         IO_ERROR     = -2
//     };
//
//     bdlde::QuotedPrintableEncoder converter;
//
//     const int INBUFFER_SIZE  = 1 << 10;
//     const int OUTBUFFER_SIZE = 1 << 10;
//
//     char inputBuffer[INBUFFER_SIZE];
//     char outputBuffer[OUTBUFFER_SIZE];
//
//     char *output    = outputBuffer;
//     char *outputEnd = outputBuffer + sizeof outputBuffer;
//
//     while (is.good()) {  // input stream not exhausted
//
//         is.read(inputBuffer, sizeof inputBuffer);
//
//         const char *input    = inputBuffer;
//         const char *inputEnd = input + is.gcount();
//
//         while (input < inputEnd) { // input encoding not complete
//
//             int numOut;
//             int numIn;
//
//             int status = converter.convert(output, &numOut, &numIn,
//                                            input,   inputEnd,
//                                            outputEnd - output);
//             if (status < 0) {
//                 return ENCODE_ERROR;                               // RETURN
//             }
//
//             output += numOut;
//             input  += numIn;
//
//             if (output == outputEnd) {  // output buffer full; write data
//                 os.write(outputBuffer, sizeof outputBuffer);
//                 if (os.fail()) {
//                     return IO_ERROR;                               // RETURN
//                 }
//                 output = outputBuffer;
//             }
//         }
//     }
//
//     for (;;) {
//
//         int more =
//                   converter.endConvert(output, &numOut, outputEnd - output);
//         if (more < 0) {
//             return ENCODE_ERROR;                                   // RETURN
//         }
//
//         output += numOut;
//
//         if (!more) { // no more output
//             break;
//         }
//
//         assert (output == outputEnd);  // output buffer is full
//
//         os.write (outputBuffer, sizeof outputBuffer);  // write buffer
//         if (os.fail()) {
//             return IO_ERROR;                                       // RETURN
//         }
//         output = outputBuffer;
//     }
//
//     if (output > outputBuffer) {
//         os.write (outputBuffer, output - outputBuffer);
//     }
//
//     return is.eof() && os.good() ? SUCCESS : IO_ERROR;
// }
//
// } // Close namespace BloombergLP
// ```

#include <bdlscm_version.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bsl_climits.h>  // INT_MAX

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslalg_typetraitusesbslmaallocator.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace bdlde {

                       // ============================
                       // class QuotedPrintableEncoder
                       // ============================

/// This class implements a mechanism capable of converting data of
/// arbitrary length to its corresponding Quoted-Printable representation.
class QuotedPrintableEncoder {

    // PRIVATE TYPES

    /// Symbolic state values for the encoder
    enum States {
        e_ERROR_STATE   = -1,  // input is irreparably invalid
        e_INITIAL_STATE =  0,  // require no more input
        e_INPUT_STATE   =  1,  // general input state
        e_DONE_STATE    =  2   // accepting; any additional input is error
    };

    enum {
        k_DEFAULT_MAX_LINELEN = 76  // maximum allowed by RFC 2045
    };

  public:
    // PUBLIC TYPES

    /// The input equivalence classes
    enum EquivalenceClass {
        e_PC = 0,  // printable character - copy straight to output
        e_CR,      // carriage return     - wait for more input
        e_LF,      // line feed           - complete linebreak
        e_WS,      // whitespace          - buffer; wait for more input
        e_CC       // control character   - encode to Quoted Printable
    };

    enum LineBreakMode {
        // Configuration governing how various forms of line breaks are to be
        // interpreted

        e_CRLF_MODE = 0,  // allow "\r\n" as linebreaks

        e_LF_MODE,        // allow '\n' as linebreaks (without the '\r' prefix)

        e_MIXED_MODE,     // allow "\r\n" and '\n' as linebreaks
        e_BINARY_MODE     // allow no linebreaks
    };

  private:
    // CLASS DATA
    static const char  *s_defaultEquivClass_p;      // default map of
                                                    // `unsigned char` to
                                                    // equivalent class

    static const char  *s_lineBreakModeName[];      // names of line break mode

    // INSTANCE DATA
    LineBreakMode     d_lineBreakMode;  // linebreak mode

    int               d_maxLineLength;  // maximum length of output line

    int               d_outputLength;   // total number of output characters

    int               d_lineLength;     // number of characters on the current
                                        //line

    char             *d_equivClass_p;   // map of `unsigned char` to input
                                        // equivalence class; dynamically
                                        // allocated if the default map is to
                                        // be modified; otherwise it is
                                        // assigned; compare with static
                                        // address to know whether to delete

    char              d_lastInputChar;  // stores an input space or tab if it
                                        // happens at the end of input

    int               d_state;          // stores current state of this object

    char              d_buffer[5];      // stack of characters to output

    int               d_bufferLength;   // size of the stack

    int               d_lineStart;      // index of output character that
                                        // starts the current line

    char              d_deffered;       // stores 0 or the deffered input
                                        // character

    bool              d_lastWasWS;      // stores whether last printed
                                        // character was a whitespace character

    bslma::Allocator *d_allocator_p;    // memory allocator (held, not owned)

  private:
    // PRIVATE MANIPULATORS

    /// Append to the buffer addressed by the specified `out` the first
    /// character of the soft line-break character sequence and push onto
    /// the buffered output stack the characters representing the residual
    /// of the soft line-break character sequence.
    void appendSoftLineBreak(char *out);

    /// Append to the buffer addressed by the specified `out` the first
    /// character of the hard line-break character sequence and push onto
    /// the buffered output stack the characters representing the residual
    /// of the hard line-break character sequence.
    void appendHardLineBreak(char *out);

    /// Append to the buffer addressed by the specified `out` or, through
    /// the buffered output stack, schedule to append the characters
    /// representing, if needed, a soft line-break and the specified
    /// character `ch`.
    void appendPrintable(char *out, char ch);

    /// Append to the buffer addressed by the specified `out` or, through
    /// the buffered output stack, schedule to append the characters
    /// representing, if needed, a soft line-break, with consideration for
    /// whether or not this is the final chanracters in the encoding as per
    /// the specified `isFinal`, and the specified character `ch`
    /// represented as a hexadecimal encoding.
    void appendAsHex(char *out, char ch, bool isFinal = false);

    // NOT IMPLEMENTED
    QuotedPrintableEncoder(const QuotedPrintableEncoder&);
    QuotedPrintableEncoder& operator=(const QuotedPrintableEncoder&);

  public:
    // CLASS METHODS

    /// Return the string representation exactly matching the enumerator
    /// name corresponding to the specified enumerator `mode`.
    static const char* lineBreakModeToAscii(
                                   QuotedPrintableEncoder::LineBreakMode mode);

    // CREATORS

    /// Create a Quoted-Printable encoder in the initial state, configured
    /// to accept hard line breaks based on the optionally specified
    /// `lineBreakMode`, and to insert soft line breaks when the line length
    /// exceeds the optionally specified `maxLineLength` (default is the RFC
    /// 2045 maximum 76).  Optionally specify a `basicAllocator` used to
    /// supply memory.  If `basicAllocator` is 0, the currently installed
    /// default allocator is used.  The behavior is undefined unless
    /// `4 <= maxLineLength <= 76`.  Note that e_CRLF_MODE passes "\r\n"
    /// straight to output and converts '\n'; e_LF_MODE passes '\n' and
    /// converts '\r'; e_MIXED_MODE passes both "\r\n" and '\n'.
    explicit
    QuotedPrintableEncoder(
                       LineBreakMode     lineBreakMode = e_CRLF_MODE,
                       int               maxLineLength = k_DEFAULT_MAX_LINELEN,
                       bslma::Allocator *basicAllocator = 0);

    /// Create a Quoted-Printable encoder in the initial state, configured
    /// to convert to the form "=XX" any input character matching a
    /// printable or whitespace character in the specified
    /// `extraCharsToEncode` array (as opposed to the default setting of
    /// passing the input character straight to output), to accept hard
    /// linebreaks based on the optionally specified `lineBreakMode`, and to
    /// insert soft linebreaks when the line length exceeds the optionally
    /// specified `maxLineLength` (default is the RFC 2045 maximum 76).
    /// Optionally specify a `basicAllocator` used to supply memory.  If
    /// `basicAllocator` is 0, the currently installed default allocator is
    /// used.  The behavior is undefined unless `4 <= `maxLineLength <= 76'.
    /// Note that e_CRLF_MODE passes "\r\n" straight to output and converts
    /// '\n'; e_LF_MODE passes '\n' and converts '\r'; e_MIXED_MODE passes
    /// both "\r\n" and '\n'.
    explicit
    QuotedPrintableEncoder(
                       const char       *extraCharsToEncode,
                       LineBreakMode     lineBreakMode = e_CRLF_MODE,
                       int               maxLineLength = k_DEFAULT_MAX_LINELEN,
                       bslma::Allocator *basicAllocator = 0);

    /// Destroy this object.
    ~QuotedPrintableEncoder();

    // MANIPULATORS

    /// Append to the buffer addressed by the specified `out` all pending
    /// output (if there is any) up to the optionally specified `maxNumOut`
    /// limit (default is negative, meaning no limit) and, when there is no
    /// pending output and `maxNumOut` is still not reached, begin to
    /// consume and encode a sequence of input characters starting at the
    /// specified `begin` position, up to but not including the specified
    /// `end` position, writing any resulting output in the specified `out`
    /// buffer up to the (cumulative) `maxNumOut` limit.  If `maxNumOut`
    /// limit is reached, no further input will be consumed.  Load into the
    /// specified `numOut` and `numIn` the number of output bytes produced
    /// and input bytes consumed, respectively.  Return a non-negative value
    /// on success and a negative value otherwise.  A successful return
    /// status indicates the number of characters that would be output if
    /// `endConvert` were called subsequently with no output limit.  These
    /// bytes *may* be available for output if this method is called with a
    /// sufficiently large `maxNumOut`.  Note that calling this method after
    /// `endConvert` has been invoked without an intervening `reset` call
    /// will place this instance in an error state, and return an error
    /// status.  Note also that it is recommended that after all calls to
    /// `convert` are finished, the `endConvert` method be called to
    /// complete the encoding of any unprocessed input characters.
    int convert(char       *out,
                int        *numOut,
                int        *numIn,
                const char *begin,
                const char *end,
                int         maxNumOut = -1);

    /// Terminate encoding for this encoder; write any retained output
    /// (e.g., from a previous call to `convert` with a non-zero `maxNumOut`
    /// argument) to the specified `out` buffer.  Optionally specify the
    /// `maxNumOut` limit on the number of bytes to output; if `maxNumOut`
    /// is negative, no limit is imposed.  Load into the specified `numOut`
    /// the number of output bytes produced.  Return a non-negative value on
    /// success and a negative value otherwise.  A successful return status
    /// indicates the number of characters that would be output if
    /// `endConvert` were called subsequently with no output limit.  Any
    /// retained bytes are available on a subsequent call to `endConvert`.
    /// Once this method is called, no additional input may be supplied
    /// without an intervening call to `reset`; once this method returns a
    /// zero status, a subsequent call will place this encoder in the error
    /// state, and return an error status.
    int endConvert(char *out, int *numOut, int maxNumOut = -1);

    /// Reset this encoder to its initial state (i.e., as if no input had
    /// been consumed).
    void reset();

    // ACCESSORS

    /// Return `true` if the input read so far by this encoder is considered
    /// syntactically complete, and `false` otherwise.
    bool isAccepting() const;

    /// Return `true` if this encoder is in the done state (i.e.,
    /// `endConvert` has been called and any additional input will result in
    /// an error), and if there is no pending output, and `false` otherwise.
    bool isDone() const;

    /// Return `true` if there is no possibility of achieving an
    /// "acceptable" result, and `false` otherwise.  Note that for an
    /// encoder, no input can cause an error; the possible errors result
    /// either from a call to the `convert` method after the `endConvert`
    /// method is called the first time, or from a call to either the
    /// `convert` or the `endConvert` methods after the `endConvert` method
    /// has returned successfully.
    bool isError() const;

    /// Return `true` if this encoder is in the initial state (i.e., as if
    /// no input had been consumed), and `false` otherwise.
    bool isInitialState() const;

    /// Return the line break mode configured at the construction of this
    /// encoder.
    LineBreakMode lineBreakMode() const;

    /// Return the value for the maximum line length configured at the
    /// construction of this encoder.
    int maxLineLength() const;

    /// Return the number of characters that would be output if `endConvert`
    /// were called with no output limit.
    int numOutputPending() const;

    /// Return the total length of the output emitted by this encoder
    /// (possibly after one or more calls to the `convert` or the `input`
    /// methods) since its initial construction or the latest `reset`.  Note
    /// that soft line breaks are included in the counts if added.
    int outputLength() const;
};

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

// CLASS METHODS
inline
const char* QuotedPrintableEncoder::lineBreakModeToAscii(LineBreakMode mode)
{
    return s_lineBreakModeName[mode];
}

// MANIPULATORS
inline
void QuotedPrintableEncoder::reset()
{
    d_state = e_INITIAL_STATE;
    d_outputLength = 0;
    d_lineLength = 0;
    d_bufferLength = 0;
    d_deffered = 0;
}

// ACCESSORS
inline
bool QuotedPrintableEncoder::isAccepting() const
{
    return e_ERROR_STATE != d_state;
}

inline
bool QuotedPrintableEncoder::isDone() const
{
    return e_DONE_STATE == d_state && 0 == d_deffered && 0 == d_bufferLength;
}

inline
bool QuotedPrintableEncoder::isError() const
{
    return e_ERROR_STATE == d_state;
}

inline
bool QuotedPrintableEncoder::isInitialState() const
{
    return e_INITIAL_STATE == d_state;
}

inline
QuotedPrintableEncoder::LineBreakMode
QuotedPrintableEncoder::lineBreakMode() const
{
    return d_lineBreakMode;
}

inline
int QuotedPrintableEncoder::maxLineLength() const
{
    return d_maxLineLength;
}

inline
int QuotedPrintableEncoder::numOutputPending() const
{
    return d_deffered ? 3 : d_bufferLength;
}

inline
int QuotedPrintableEncoder::outputLength() const
{
    return d_outputLength;
}

}  // close package namespace
}  // close enterprise namespace

// TRAITS

namespace BloombergLP {
namespace bslma {

template <>
struct UsesBslmaAllocator<bdlde::QuotedPrintableEncoder> : bsl::true_type {};

}  // close namespace bslma
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2016 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
