// balxml_hexparser.h                                                 -*-C++-*-
#ifndef INCLUDED_BALXML_HEXPARSER
#define INCLUDED_BALXML_HEXPARSER

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

//@PURPOSE: Provide push parser for hex types.
//
//@CLASSES:
//   balxml::HexParser: push parser for hex types
//
//@SEE_ALSO:
//
//@DESCRIPTION: The `balxml::HexParser` class template provided by this
// component can be used to parse Hex characters into one of the supported Hex
// types, which are `bsl::vector<char>` and `bsl::string`.  The `TYPE`
// parameter can be one of these two types.
//
// Note that if you need a way to encode binary data into ASCII, the
// `bdlde_base64encoder` and `bdlde_base64decoder` components are likely a more
// efficient solution.
//
// This class template is a model of the `PushParser` concept, which contains
// the following methods:
// ```
// int beginParse(TYPE *object);
//     // Prepare the parser to start parsing a new value and associate the
//     // specified 'object' with the parser.  Return 0 if successful and
//     // non-zero otherwise.
//
// int endParse();
//     // Ends the parse operation and store the value parsed from the pushed
//     // characters into the associated object.  Return 0 if successful and
//     // non-zero otherwise.  The behavior is undefined unless an object is
//     // associated with this parser.  Upon successful completion, the parser
//     // will be disassociated with the object.
//
// template <typename INPUT_ITERATOR>
// int pushCharacters(INPUT_ITERATOR begin, INPUT_ITERATOR end);
//     // Push the characters ranging from the specified 'begin' up to (but
//     // not including) the specified 'end' into this parser.  Return 0 if
//     // successful and non-zero otherwise.  The parameterized
//     // 'INPUT_ITERATOR' must be dereferenceable to a 'char' value.  The
//     // behavior is undefined unless an object is associated with this
//     // parser.
// ```
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Usage
/// - - - - - - - - - - -
// The following snippets of code illustrate the usage of this component.
// Suppose you had an input stream that contained Hex data.  The following
// `loadFromHexStream` function loads this data into an `bsl::vector<char>`
// blob:
// ```
// #include <balxml_hexparser.h>
//
// #include <bsl_istream.h>
// #include <bsl_iterator.h>
// #include <bsl_vector.h>
//
// using namespace BloombergLP;
//
// int loadFromHexStream(bsl::vector<char> *result, bsl::istream& stream)
// {
//     enum { k_FAILURE = -1 };
//
//     balxml::HexParser<bsl::vector<char> > parser;
//
//     if (0 != parser.beginParse(result)) {
//         return k_FAILURE;
//     }
//
//     if (0 != parser.pushCharacters(bsl::istreambuf_iterator<char>(stream),
//                                    bsl::istreambuf_iterator<char>())) {
//         return k_FAILURE;
//     }
//
//     return parser.endParse();
// }
// ```
// The following function demonstrates the `loadFromHexStream` function:
// ```
// #include <sstream>
//
// void usageExample()
// {
//     const char INPUT[] = "0F3B296A";
//
//     bsl::vector<char>  vec;
//     bsl::istringstream iss(INPUT);
//
//     int result = loadFromHexStream(&vec, iss);
//
//     assert(0    == result);
//     assert(4    == vec.size());
//     assert(0x0F == vec[0]);
//     assert(0x3B == vec[1]);
//     assert(0x29 == vec[2]);
//     assert(0x6A == vec[3]);
// }
// ```

#include <balscm_version.h>

#include <bdlat_valuetypefunctions.h>

#include <bdlb_chartype.h>

#include <bsls_assert.h>
#include <bsls_review.h>

namespace BloombergLP {
namespace balxml {

                           // =====================
                           // class HexParser<TYPE>
                           // =====================

/// This is a push parser for supported Hex types (`bsl::vector<char>` or
/// `bsl::string`).
template <class TYPE>
class HexParser {

    // PRIVATE DATA MEMBERS
    char  d_firstDigit;  // buffer for first digit
    TYPE *d_object_p;    // associated object

  private:
    // NOT IMPLEMENTED
    HexParser(const HexParser&);
    HexParser& operator=(const HexParser&);

    // PRIVATE MANIPULATORS

    /// Append an octet generated from the specified `firstDigit` and the
    /// specified `secondDigit` to the associated object.
    void appendOctet(char firstDigit, char secondDigit);

  public:
    // CREATORS

    /// Create a parser for parsing Hex types.
    HexParser();

    // Generated by compiler:
    //  ~HexParser();

    // MANIPULATORS

    /// Prepare the parser to start parsing a new value and associate the
    /// specified `object` with the parser.  Return 0 if successful and
    /// non-zero otherwise.
    int beginParse(TYPE *object);

    /// Ends the parse operation and store the value parsed from the pushed
    /// characters into the associated object.  Return 0 if successful and
    /// non-zero otherwise.  The behavior is undefined unless an object is
    /// associated with this parser.  Upon successful completion, the parser
    /// will be disassociated with the object.
    int endParse();

    /// Push the characters ranging from the specified `begin` up to (but
    /// not including) the specified `end` into this parser.  Return 0 if
    /// successful and non-zero otherwise.  The parameterized
    /// `INPUT_ITERATOR` must be dereferenceable to a `char` value.  The
    /// behavior is undefined unless an object is associated with this
    /// parser.
    template <class INPUT_ITERATOR>
    int pushCharacters(INPUT_ITERATOR begin, INPUT_ITERATOR end);
};

                          // =======================
                          // struct HexParser_Helper
                          // =======================

/// Namespace for facilities that are used in the implementation of class
/// `HexParser<TYPE>`.  Only instances of `HexParser<TYPE>` can access the
/// facilities in this class.
class HexParser_Helper {

    template <class TYPE>
    friend class HexParser;

    // Table that maps from ASCII character value to hex value.
    static const char s_hexValueTable[128];
};

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

                           // ---------------------
                           // class HexParser<TYPE>
                           // ---------------------

// PRIVATE MANIPULATORS
template <class TYPE>
void HexParser<TYPE>::appendOctet(char firstDigit, char secondDigit)
{
    BSLS_ASSERT(bdlb::CharType::isXdigit((unsigned char) firstDigit));
    BSLS_ASSERT(bdlb::CharType::isXdigit((unsigned char) secondDigit));

    char value =
        (char)((HexParser_Helper::s_hexValueTable[(int)firstDigit] << 4)
             | (HexParser_Helper::s_hexValueTable[(int)secondDigit]));

    d_object_p->push_back(value);
}

// CREATORS
template <class TYPE>
HexParser<TYPE>::HexParser()
: d_firstDigit(0)
, d_object_p(0)
{
}

// MANIPULATORS
template <class TYPE>
int HexParser<TYPE>::beginParse(TYPE *object)
{
    BSLS_ASSERT(object);

    enum { k_SUCCESS = 0 };

    d_firstDigit = 0;
    d_object_p   = object;

    bdlat_ValueTypeFunctions::reset(d_object_p);

    return k_SUCCESS;
}

template <class TYPE>
int HexParser<TYPE>::endParse()
{
    BSLS_ASSERT(d_object_p);

    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    d_object_p = 0;

    return 0 == d_firstDigit ? k_SUCCESS : k_FAILURE;
}

template <class TYPE>
template <class INPUT_ITERATOR>
int HexParser<TYPE>::pushCharacters(INPUT_ITERATOR begin, INPUT_ITERATOR end)
{
    BSLS_ASSERT(d_object_p);

    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    while (begin != end) {
        const char digit = *begin;

        ++begin;

        if (!bdlb::CharType::isSpace(digit)) {
            if (!bdlb::CharType::isXdigit(digit)) {
                return k_FAILURE;                                     // RETURN
            }

            if (0 == d_firstDigit) {
                d_firstDigit = digit;
            }
            else {
                appendOctet(d_firstDigit, digit);

                d_firstDigit = 0;
            }
        }
    }

    return k_SUCCESS;
}

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

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 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 ----------------------------------
