// balxml_hexparser.h                                                 -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#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
///-----
// 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>
                           // =====================

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

    // 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
    void appendOctet(char firstDigit, char secondDigit);
        // Append an octet generated from the specified 'firstDigit' and the
        // specified 'secondDigit' to the associated object.

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

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

    // MANIPULATORS
    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 <class 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.
};

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

class 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.

    template <class TYPE>
    friend class HexParser;

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

// ============================================================================
//                            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 ----------------------------------