// bdlb_print.h                                                       -*-C++-*-
#ifndef INCLUDED_BDLB_PRINT
#define INCLUDED_BDLB_PRINT

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

//@PURPOSE: Provide platform-independent stream utilities.
//
//@CLASSES:
//  bdlb::Print: namespace for procedures on streams
//  bdlb::PrintStringHexDumper: create/print hex buffers, multi-line
//  bdlb::PrintStringSingleLineHexDumper: create/print hex buffers, single line
//
//@DESCRIPTION: This component provides a namespace, `bdlb::Print`, containing
// utility functions for formatting data to `bsl::ostream` objects.  These
// functions provide several variations of hexadecimal format, allow
// platform-independent representation of `void *` pointers, and can help with
// the indentation of hierarchical data.
//
// This component also provides two helper classes,
// `bdlb::PrintStringHexDumper` and `bdlb::PrintStringSingleLineHexDumper`,
// that define `operator<<` so they can be used in chains of `<<` operations.
// The `bdlb::PrintStringHexDumper` class produces formatted, possibly
// multi-line output, whereas the `bdlb::PrintStringSingleLineHexDumper` class
// produces a simple sequence of hexadecimal digits (and no newline).
//
///`xxd`-Compatible `hexDump`
/// - - - - - - - - - - - - -
// The output generated by the `hexDump` functions is not `xxd`-compatible (see
// `http://gd.tuwien.ac.at/linuxcommand.org/man_pages/xxd1.html`).  The
// following perl script is provided that will convert `hexDump` output into
// `xxd`-compatible form.  Run the script with a file containing the `hexDump`
// output as the first argument.
// ```
// #!/usr/bin/perl -w
//
// use strict;
//
// my $num = 0;
// while (<>) {
//     next if (!$_);
//     my $str = $_;
//     next if !($str =~ s/^[^:]*?:\s*//);
//     my $h = sprintf("%08X",$num);
//     $str =~ s/(\S{4})([\S\W]{4})\s?([\S\W]{4})([\S\W]{4})\s?([\S\W]{4})?
//           ([\S\W]{4})?\s?([\S\W]{4})?([\S\W]{4})?/$1 $2 $3 $4 $5 $6 $7 $8/;
//     $str =~ s/\s \|([^|]+)\|.*$/ $1/;
//     print "$h: ";
//     print $str;
//     $num = $num + 16;
// }
// ```
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Using `printPtr`
///- - - - - - - - - - - - - -
// The default output produced from pointer values is non-standard across
// vendor platforms.  The `printPtr` method addresses this inconsistency by
// always producing a consistent format for a given pointer size:
// ```
// const void *a = reinterpret_cast<void *>(0x0);
// const void *b = reinterpret_cast<void *>(0xf2ff);
// const void *c = reinterpret_cast<void *>(0x0123);
// const void *d = reinterpret_cast<void *>(0xf1f2abc9);
//
// bsl::ostringstream out1;
//
// bdlb::Print::printPtr(out1, a); out1 << endl;
// bdlb::Print::printPtr(out1, b); out1 << endl;
// bdlb::Print::printPtr(out1, c); out1 << endl;
// bdlb::Print::printPtr(out1, d); out1 << endl;
//
// assert("0"        "\n"
//        "f2ff"     "\n"
//        "123"      "\n"
//        "f1f2abc9" "\n" == out1.str());
// ```
//
///Example 2: Using the Helper Classes
///- - - - - - - - - - - - - - - - - -
// The two helper classes allow users to stream a hexadecimal representation
// of a sequence of bytes into an output stream.
//
// The `bdlb::PrintStringHexDumper` provides a formatted, possibly multi-line
// representation:
// ```
// char buf[] = "abcdefghijklmnopqrstuvwxyz";
//
// bsl::ostringstream out2a;
// out2a << bdlb::PrintStringHexDumper(buf, sizeof buf);
//
// assert(
//    "     0:   61626364 65666768 696A6B6C 6D6E6F70     |abcdefghijklmnop|\n"
//    "    16:   71727374 75767778 797A00                |qrstuvwxyz.     |\n"
//     == out2a.str());
//
// bsl::ostringstream out2b;
// out2b << bdlb::PrintStringSingleLineHexDumper(buf, sizeof buf);
// ```
// The `bdlb::PrintStringSingleLineHexDumper` provides a simple, single-line
// representation.
// ```
// assert("6162636465666768696A6B6C6D6E6F707172737475767778797A00"
//     == out2b.str());
// ```

#include <bdlscm_version.h>

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

#include <bsl_ostream.h>
#include <bsl_utility.h>

namespace BloombergLP {
namespace bdlb {
                                // ============
                                // struct Print
                                // ============

/// Provide a namespace for the interface to a suite of procedural stream
/// operations.
struct Print {

    // CLASS METHODS

    /// Emit to the specified output `stream` the number of spaces (` `)
    /// equal to the absolute value of the product of the specified `level`
    /// and `spacesPerLevel` or, if `level` is negative, nothing at all.
    /// Return a reference providing modifiable access to `stream`.  The
    /// behavior is undefined unless the absolute value of the product of
    /// the specified `level` and `spacesPerLevel` is representable as
    /// `int`.
    static bsl::ostream& indent(bsl::ostream& stream,
                                int           level,
                                int           spacesPerLevel = 4);

    /// Emit to the specified `stream` a newline ('\n') followed by the
    /// number of spaces (` `) equal to the absolute value of the product
    /// of the specified `level` and `spacesPerLevel` or, if
    /// `spacesPerLevel` is negative, emit a single space (and *no*
    /// newline).  Return a reference providing modifiable access to
    /// `stream`.  The behavior is undefined unless the absolute value of
    /// the product of the specified `level` and `spacesPerLevel` is
    /// representable as `int`.
    static bsl::ostream& newlineAndIndent(bsl::ostream& stream,
                                          int           level,
                                          int           spacesPerLevel = 4);

    /// Print to the specified `stream` the specified pointer `value` in a
    /// standard format.  The output is in hexadecimal format with a maximum
    /// length of `2 * sizeof(void *)`.  The output does not have leading
    /// zeros and is not preceded by `0x`.  The hexadecimal digits (`a` to
    /// `f`, inclusive) are expressed in lower case.
    static void printPtr(bsl::ostream& stream, const void *value);

    /// Print to the specified `stream` the specified `string` of the
    /// specified `length` and return a reference providing modifiable
    /// access to `stream`.  If the optionally specified `escapeBackSlash`
    /// flag is `true`, then all occurrences of the backslash character
    /// ('\') in the `string` are escaped (i.e., expanded to "\\") when
    /// written to the `stream`.  Note that non-printable characters in
    /// `string` will be printed in their hexadecimal representation
    /// ('\xHH').  If `stream` is not valid on entry, this operation has no
    /// effect.  The behavior is undefined unless `0 <= length`.
    static bsl::ostream& printString(bsl::ostream&  stream,
                                     const char    *string,
                                     int            length,
                                     bool           escapeBackSlash = false);

    /// Print in hexadecimal format the contents of the specified `buffer`
    /// of the specified `length` to the specified `stream`, and return a
    /// reference providing modifiable access to `stream`.  The behavior is
    /// undefined unless `0 <= length`.
    static bsl::ostream& hexDump(bsl::ostream&  stream,
                                 const char    *buffer,
                                 int            length);

    /// Print to the specified `stream` the specified `numBuffers` buffers
    /// supplied by specified `buffers` in a hexadecimal representation (16
    /// chars per line) followed by the ASCII representation.  Return a
    /// reference providing modifiable access to `stream`.  The array of
    /// buffers are supplied as a `bsl::pair<const char*, int> *` where the
    /// first element is a pointer to the data, and the second element is
    /// the length of the buffer.  The behavior is undefined unless
    /// `0 <= numBuffers`.  Note that the contents of the buffers are
    /// concatenated and boundaries between buffers are not demarcated.
    static bsl::ostream& hexDump(bsl::ostream&                  stream,
                                 bsl::pair<const char *, int > *buffers,
                                 int                            numBuffers);

    /// Print to the specified `stream` the uppercase hex encoding of the
    /// byte sequence defined by the specified `begin` and `end` iterators
    /// of the parameterized `INPUT_ITERATOR` type, and return a reference
    /// providing modifiable access to `stream`.  Note that `INPUT_ITERATOR`
    /// need not be random-access, i.e., it need support only increment
    /// (`++`) and equality comparison (`==`).  See the non-template version
    /// of this function if insulation and/or code bloat are a concern.
    template <class INPUT_ITERATOR>
    static bsl::ostream& singleLineHexDump(bsl::ostream&  stream,
                                           INPUT_ITERATOR begin,
                                           INPUT_ITERATOR end);

    /// Print to the specified `stream` the uppercase hex encoding of the
    /// byte sequence defined by the specified `begin` and `end` iterators
    /// into the specified `stream`, and return a reference providing
    /// modifiable access to `stream`.  This function insulates clients from
    /// its implementation, but unlike the member template version (above),
    /// requires random access iterators of type `const char *`.  The
    /// behavior is undefined unless both `begin` and `end` refer to the
    /// same block of contiguous memory, and `begin <= end`.
    static bsl::ostream& singleLineHexDump(bsl::ostream&  stream,
                                           const char    *begin,
                                           const char    *end);

    /// Print to the specified `stream` the contents of the specified
    /// `buffer` having the specified `length` on a single line, and return
    /// a reference to the modifiable `stream`.  The behavior is undefined
    /// unless `0 <= length`.
    static bsl::ostream& singleLineHexDump(bsl::ostream&  stream,
                                           const char    *buffer,
                                           int            length);
};

                        // ===========================
                        // struct PrintStringHexDumper
                        // ===========================

/// Utility for hex dumping a blob to standard output streams.  This class
/// has `operator<<` defined for it, so it can be used as follows:
/// ```
/// bsl::vector<char> blob;
/// blob.resize(1024);
///
/// // ... fill up the blob with some data ...
///
/// bsl::cout << PrintStringHexDumper(blob.data(), blob.size())
///           << bsl::endl;
/// ```
struct PrintStringHexDumper {

    // DATA
    const char *d_data_p;
    int         d_length;

    // CREATORS

    /// Create a `PrintStringHexDumper` object that can insert to an output
    /// stream a formated (possibly multi-lined) hexadecimal representation
    /// the specified `data` of the specified `length`.
    PrintStringHexDumper(const char *data, int length);
};

// FREE OPERATORS

/// Hex dump the data referenced by the specified `rhs` to the specified
/// `stream`.
inline
bsl::ostream& operator<<(bsl::ostream&               stream,
                         const PrintStringHexDumper& rhs);

                   // =====================================
                   // struct PrintStringSingleLineHexDumper
                   // =====================================

/// Utility for hex dumping a string with no extra formatting to standard
/// output streams.  This class has `operator<<` defined for it, so it can
/// be used as follows:
/// ```
/// bsl::string str;
///
/// // ... fill up the str with some data ...
///
/// bsl::cout
///        << PrintStringSingleLineHexDumper(str.c_str(), str.size())
///        << bsl::endl;
/// ```
struct PrintStringSingleLineHexDumper {

    // DATA
    const char *d_data_p;
    int         d_length;

    // CREATORS

    /// Create a `PrintStringSingleLineHexDumper` object that can insert to
    /// an output stream a single-line hexadecimal representation the
    /// specified `data` of the specified `length`.
    PrintStringSingleLineHexDumper(const char *data, int length);
};

// FREE OPERATORS

/// Hex dump the data referenced by the specified `rhs` to the specified
/// `stream`.
inline
bsl::ostream& operator<<(bsl::ostream&                         stream,
                         const PrintStringSingleLineHexDumper& rhs);

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

                        // ------------
                        // struct Print
                        // ------------

// CLASS METHODS
template <class INPUT_ITERATOR>
bsl::ostream& Print::singleLineHexDump(bsl::ostream&  stream,
                                       INPUT_ITERATOR begin,
                                       INPUT_ITERATOR end)
{
    enum { k_LOCAL_BUF_SIZE = 512 };
    static const char HEX[] = "0123456789ABCDEF";

    char buf[k_LOCAL_BUF_SIZE];

    unsigned int offset = 0;

    for (; begin != end; ++begin) {

        if (offset >= (k_LOCAL_BUF_SIZE - 1)) {
             stream.write(buf, offset);
             offset = 0;
        }

        const unsigned char c = *begin;

        buf[offset++] = HEX[(c >> 4) & 0xF];
        buf[offset++] = HEX[c        & 0xF];
    }

    if (offset != 0) {
        stream.write(buf, offset);
    }

    return stream;
}

inline
bsl::ostream& Print::singleLineHexDump(bsl::ostream&  stream,
                                       const char    *buffer,
                                       int            length)
{
    BSLS_REVIEW(buffer);
    BSLS_REVIEW(0 <= length);

    return singleLineHexDump(stream, buffer, buffer + length);
}

                        // ---------------------------
                        // struct PrintStringHexDumper
                        // ---------------------------

// CREATORS
inline
PrintStringHexDumper::PrintStringHexDumper(const char *data,
                                           int         length)
: d_data_p(data)
, d_length(length)
{
    BSLS_REVIEW(data);
    BSLS_REVIEW(0 <= length);

}
}  // close package namespace

// FREE OPERATORS
inline
bsl::ostream& bdlb::operator<<(bsl::ostream&               stream,
                               const PrintStringHexDumper& rhs)
{
    return Print::hexDump(stream, rhs.d_data_p, rhs.d_length);
}

namespace bdlb {
                        // -------------------------------------
                        // struct PrintStringSingleLineHexDumper
                        // -------------------------------------

// CREATORS
inline
PrintStringSingleLineHexDumper::PrintStringSingleLineHexDumper(
                                                            const char *data,
                                                            int         length)
: d_data_p(data)
, d_length(length)
{
    BSLS_REVIEW(data);
    BSLS_REVIEW(0 <= length);
}
}  // close package namespace

// FREE OPERATORS
inline
bsl::ostream& bdlb::operator<<(bsl::ostream&                         stream,
                               const PrintStringSingleLineHexDumper& rhs)
{
    return Print::singleLineHexDump(stream, rhs.d_data_p, rhs.d_length);
}

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