#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