Newer
Older
libj / modules / j / base / src / memory_entry.cpp
#include <iostream>
#include <sstream>
#include <iomanip>
#include <string_view>

#include <cstring>

#include <j/memory_entry.hpp>

#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.')

namespace j
{
    namespace
    {
        constexpr int MAX_BUFFER_SIZE = 1024;
    }

    /**
     * 指定された出力ストリームにメモリエントリの情報を出力します。
     *
     * @param os 出力ストリーム
     * @param entry メモリエントリ
     * @param bytes 出力するバイト最大バイト数
     * @param binary true の場合、データの16進数情報が出力されます。
     * @param ascii  true の場合、データのASCII 情報が出力されます。
     * @param maxColumn 出力文字数
     * @param noEndl 改行出力無し
     */
    void MemoryEntry::dump(
        std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const
    {
        // <ファイル名>:<行番号> (<size> bytes) [<関数名>] 部分のカラム数を算出する。
        int infoColumn = maxColumn;
        infoColumn -= (binary) ? (bytes * 3) + 3 : 0;
        infoColumn -= (ascii) ? (bytes) + 3 : 0;

        if (infoColumn <= 0)
        { // カラム数不足のため、基本データのみを出力
            printBase(os, maxColumn);
        }
        else
        { // 基本データ出力
            printBase(os, infoColumn);

            if (binary)
            { // 16進数ダンプ
                os << " | ";
                printBinary(os, bytes);
            }

            if (ascii)
            { // ASCII出力
                os << " | ";
                printAscii(os, bytes);
            }
        }
        // 改行出力
        if (!noEndl)
        {
            os << std::endl;
        }
    }

    /**
     * <ファイル名>:<行番号> (<size> bytes) [<関数名>] を指定されたストリームに出力します。
     * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。
     *
     * @param os 出力ストリーム
     * @param column カラム数
     */
    void MemoryEntry::printBase(std::ostream &os, int column) const
    {
        char buff[MAX_BUFFER_SIZE];
        int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1;
        int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func);

        os << buff;
        if (baseLength < maxColumn)
        { // パディング出力
            int padding = maxColumn - baseLength;
            memset(buff, ' ', padding);
            buff[padding] = '\0';
            os << buff;
        }
    }

    /**
     * 指定された出力ストリームにバイナリの16進数ダンプを出力します。
     *
     * @param os 出力ストリーム
     * @param bytes 出力するバイト数
     */
    void MemoryEntry::printBinary(std::ostream &os, int bytes) const
    {
        os << std::hex << std::setfill('0');
        int dataLen = (this->size < bytes) ? this->size : bytes;
        unsigned char *dataPtr = static_cast<unsigned char *>(this->_data);
        int idx = 0;
        for (; idx < dataLen; idx++)
        {
            os << std::setw(2) << (dataPtr[idx] & 0xFF) << " ";
        }
        for (; idx < bytes; idx++)
        {
            os << "-- ";
        }
    }

    /**
     * 指定された出力ストリームに ASCII を出力します。
     *
     * @param os 出力ストリーム
     * @param bytes 出力するバイト数
     */
    void MemoryEntry::printAscii(std::ostream &os, int bytes) const
    {
        int dataLen = (this->size < bytes) ? this->size : bytes;
        char *dataPtr = static_cast<char *>(this->_data);
        int idx = 0;
        for (; idx < dataLen; idx++)
        {
            os << PRINT_ASCII(dataPtr[idx]);
        }
        for (; idx < bytes; idx++)
        {
            os << " ";
        }
    }

    /**
     * サイズ情報取得
     */
    const char *MemoryEntry::getSizeStr() const
    {
        static char sizeStr[16];
        static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"};
        int unitIndex = 0;
        double viewSize = (double)this->size;
        while (viewSize >= 1024)
        {
            viewSize /= 1024;
            unitIndex++;
        }
        snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]);
        return sizeStr;
    }

} // namespace j