cbeed6396f
Merge commit '6fa8d51479e9a5542c67bec715a1f68e7ed057ba'
588 lines
21 KiB
C
588 lines
21 KiB
C
/***************************************************************************************************
|
|
|
|
Zyan Disassembler Library (Zydis)
|
|
|
|
Original Author : Florian Bernd
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
|
|
***************************************************************************************************/
|
|
|
|
#include <inttypes.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <Zycore/API/Terminal.h>
|
|
#include <Zycore/LibC.h>
|
|
#include <Zydis/Zydis.h>
|
|
|
|
#if defined(ZYAN_WINDOWS)
|
|
# include <windows.h>
|
|
#elif defined(ZYAN_APPLE)
|
|
# include <mach/mach_time.h>
|
|
#elif defined(ZYAN_LINUX) || defined(ZYAN_SOLARIS)
|
|
# include <sys/time.h>
|
|
# include <pthread.h>
|
|
#elif defined(ZYAN_FREEBSD)
|
|
# include <sys/time.h>
|
|
# include <pthread.h>
|
|
# include <pthread_np.h>
|
|
#else
|
|
# error "Unsupported platform detected"
|
|
#endif
|
|
|
|
/* ============================================================================================== */
|
|
/* Colors */
|
|
/* ============================================================================================== */
|
|
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
/* Configuration */
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
|
|
#define COLOR_DEFAULT ZYAN_VT100SGR_FG_DEFAULT
|
|
#define COLOR_ERROR ZYAN_VT100SGR_FG_BRIGHT_RED
|
|
#define COLOR_VALUE_R ZYAN_VT100SGR_FG_BRIGHT_RED
|
|
#define COLOR_VALUE_G ZYAN_VT100SGR_FG_BRIGHT_GREEN
|
|
#define COLOR_VALUE_B ZYAN_VT100SGR_FG_CYAN
|
|
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
/* Global variables */
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
|
|
static ZyanBool g_vt100_stdout;
|
|
static ZyanBool g_vt100_stderr;
|
|
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
/* Helper macros */
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
* Conditionally expands to the passed VT100 sequence, if `g_colors_stdout` is
|
|
* `ZYAN_TRUE`, or an empty string, if not.
|
|
*
|
|
* @param The VT100 SGT sequence.
|
|
*/
|
|
#define CVT100_OUT(sequence) (g_vt100_stdout ? (sequence) : "")
|
|
|
|
/**
|
|
* Conditionally expands to the passed VT100 sequence, if `g_colors_stderr` is
|
|
* `ZYAN_TRUE`, or an empty string, if not.
|
|
*
|
|
* @param The VT100 SGT sequence.
|
|
*/
|
|
#define CVT100_ERR(sequence) (g_vt100_stderr ? (sequence) : "")
|
|
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
|
|
/* ============================================================================================== */
|
|
/* Helper functions */
|
|
/* ============================================================================================== */
|
|
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
/* Time measurement */
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
|
|
#if defined(ZYAN_WINDOWS)
|
|
|
|
double counter_freq = 0.0;
|
|
ZyanU64 counter_start = 0;
|
|
|
|
static void StartCounter(void)
|
|
{
|
|
LARGE_INTEGER li;
|
|
if (!QueryPerformanceFrequency(&li))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sError: QueryPerformanceFrequency failed!%s\n",
|
|
CVT100_ERR(COLOR_ERROR), CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
counter_freq = (double)li.QuadPart / 1000.0;
|
|
QueryPerformanceCounter(&li);
|
|
counter_start = li.QuadPart;
|
|
}
|
|
|
|
static double GetCounter(void)
|
|
{
|
|
LARGE_INTEGER li;
|
|
QueryPerformanceCounter(&li);
|
|
return (double)(li.QuadPart - counter_start) / counter_freq;
|
|
}
|
|
|
|
#elif defined(ZYAN_APPLE)
|
|
|
|
ZyanU64 counter_start = 0;
|
|
mach_timebase_info_data_t timebase_info;
|
|
|
|
static void StartCounter(void)
|
|
{
|
|
counter_start = mach_absolute_time();
|
|
}
|
|
|
|
static double GetCounter(void)
|
|
{
|
|
ZyanU64 elapsed = mach_absolute_time() - counter_start;
|
|
|
|
if (timebase_info.denom == 0)
|
|
{
|
|
mach_timebase_info(&timebase_info);
|
|
}
|
|
|
|
return (double)elapsed * timebase_info.numer / timebase_info.denom / 1000000;
|
|
}
|
|
|
|
#elif defined(ZYAN_LINUX) || defined(ZYAN_FREEBSD) || defined(ZYAN_SOLARIS)
|
|
|
|
struct timeval t1;
|
|
|
|
static void StartCounter(void)
|
|
{
|
|
gettimeofday(&t1, NULL);
|
|
}
|
|
|
|
static double GetCounter(void)
|
|
{
|
|
struct timeval t2;
|
|
gettimeofday(&t2, NULL);
|
|
|
|
double t = (t2.tv_sec - t1.tv_sec) * 1000.0;
|
|
return t + (t2.tv_usec - t1.tv_usec) / 1000.0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
/* Process & Thread Priority */
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
|
|
static void AdjustProcessAndThreadPriority(void)
|
|
{
|
|
#if defined(ZYAN_WINDOWS)
|
|
|
|
SYSTEM_INFO info;
|
|
GetSystemInfo(&info);
|
|
if (info.dwNumberOfProcessors > 1)
|
|
{
|
|
if (!SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sWarning: Could not set thread affinity mask%s\n",
|
|
CVT100_ERR(ZYAN_VT100SGR_FG_YELLOW), CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
}
|
|
if (!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sWarning: Could not set process priority class%s\n",
|
|
CVT100_ERR(ZYAN_VT100SGR_FG_YELLOW), CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
}
|
|
if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sWarning: Could not set thread priority class%s\n",
|
|
CVT100_ERR(ZYAN_VT100SGR_FG_YELLOW), CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
}
|
|
}
|
|
|
|
#elif defined(ZYAN_LINUX) || defined(ZYAN_FREEBSD)
|
|
|
|
pthread_t thread = pthread_self();
|
|
|
|
#if defined(ZYAN_LINUX)
|
|
cpu_set_t cpus;
|
|
#else // FreeBSD
|
|
cpuset_t cpus;
|
|
#endif
|
|
|
|
CPU_ZERO(&cpus);
|
|
CPU_SET(0, &cpus);
|
|
if (pthread_setaffinity_np(thread, sizeof(cpus), &cpus))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sWarning: Could not set thread affinity mask%s\n",
|
|
CVT100_ERR(ZYAN_VT100SGR_FG_YELLOW), CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------- */
|
|
|
|
/* ============================================================================================== */
|
|
/* Internal functions */
|
|
/* ============================================================================================== */
|
|
|
|
typedef struct TestContext_
|
|
{
|
|
ZydisDecoderContext context;
|
|
ZydisDecodedInstruction instruction;
|
|
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
|
|
char format_buffer[256];
|
|
ZyanBool minimal_mode;
|
|
ZyanBool format;
|
|
ZyanBool tokenize;
|
|
} TestContext;
|
|
|
|
static ZyanU64 ProcessBuffer(const ZydisDecoder* decoder, const ZydisFormatter* formatter,
|
|
TestContext* context, const ZyanU8* buffer, ZyanUSize length)
|
|
{
|
|
ZyanU64 count = 0;
|
|
ZyanUSize offset = 0;
|
|
ZyanStatus status;
|
|
|
|
while (length > offset)
|
|
{
|
|
status = ZydisDecoderDecodeInstruction(decoder, &context->context, buffer + offset,
|
|
length - offset, &context->instruction);
|
|
|
|
if (status == ZYDIS_STATUS_NO_MORE_DATA)
|
|
{
|
|
break;
|
|
}
|
|
if (!context->minimal_mode && ZYAN_SUCCESS(status))
|
|
{
|
|
status = ZydisDecoderDecodeOperands(decoder, &context->context, &context->instruction,
|
|
context->operands, context->instruction.operand_count);
|
|
}
|
|
if (!ZYAN_SUCCESS(status))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sUnexpected decoding error. Data: ",
|
|
CVT100_ERR(COLOR_ERROR));
|
|
for (ZyanUSize i = 0; i < ZYAN_MIN(ZYDIS_MAX_INSTRUCTION_LENGTH,
|
|
length - offset); ++i)
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%02X ", (ZyanU8)buffer[offset + i]);
|
|
}
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%s\n", CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
ZYAN_ASSERT(ZYAN_FALSE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (context->format)
|
|
{
|
|
if (context->tokenize)
|
|
{
|
|
const ZydisFormatterToken* token;
|
|
ZydisFormatterTokenizeInstruction(formatter, &context->instruction,
|
|
context->operands, context->instruction.operand_count_visible,
|
|
context->format_buffer, sizeof(context->format_buffer), offset, &token,
|
|
ZYAN_NULL);
|
|
} else
|
|
{
|
|
ZydisFormatterFormatInstruction(formatter, &context->instruction,
|
|
context->operands, context->instruction.operand_count_visible,
|
|
context->format_buffer, sizeof(context->format_buffer), offset, ZYAN_NULL);
|
|
}
|
|
}
|
|
|
|
offset += context->instruction.length;
|
|
++count;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static void TestPerformance(const ZyanU8* buffer, ZyanUSize length, ZyanBool minimal_mode,
|
|
ZyanBool format, ZyanBool tokenize, ZyanBool use_cache)
|
|
{
|
|
ZydisDecoder decoder;
|
|
if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64,
|
|
ZYDIS_STACK_WIDTH_64)))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sFailed to initialize decoder%s\n",
|
|
CVT100_ERR(COLOR_ERROR), CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (!ZYAN_SUCCESS(ZydisDecoderEnableMode(&decoder, ZYDIS_DECODER_MODE_MINIMAL, minimal_mode)))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sFailed to adjust decoder-mode%s\n",
|
|
CVT100_ERR(COLOR_ERROR), CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// ZydisCacheTable cache;
|
|
// if (use_cache && !ZYAN_SUCCESS(ZydisDecoderInitCache(&decoder, &cache)))
|
|
// {
|
|
// ZYAN_FPRINTF(ZYAN_STDERR, "%sFailed to initialize decoder-cache%s\n",
|
|
// CVT100_ERR(COLOR_ERROR), CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
// exit(EXIT_FAILURE);
|
|
// }
|
|
|
|
ZydisFormatter formatter;
|
|
if (format)
|
|
{
|
|
if (!ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL)) ||
|
|
!ZYAN_SUCCESS(ZydisFormatterSetProperty(&formatter,
|
|
ZYDIS_FORMATTER_PROP_FORCE_SEGMENT, ZYAN_TRUE)) ||
|
|
!ZYAN_SUCCESS(ZydisFormatterSetProperty(&formatter,
|
|
ZYDIS_FORMATTER_PROP_FORCE_SIZE, ZYAN_TRUE)))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sFailed to initialize instruction-formatter%s\n",
|
|
CVT100_ERR(COLOR_ERROR), CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
TestContext context;
|
|
context.minimal_mode = minimal_mode;
|
|
context.format = format;
|
|
context.tokenize = tokenize;
|
|
|
|
// Cache warmup
|
|
ProcessBuffer(&decoder, &formatter, &context, buffer, length);
|
|
|
|
// Testing
|
|
ZyanU64 count = 0;
|
|
StartCounter();
|
|
for (ZyanU8 j = 0; j < 100; ++j)
|
|
{
|
|
count += ProcessBuffer(&decoder, &formatter, &context, buffer, length);
|
|
}
|
|
const char* color[4];
|
|
color[0] = minimal_mode ? CVT100_OUT(COLOR_VALUE_G) : CVT100_OUT(COLOR_VALUE_B);
|
|
color[1] = format ? CVT100_OUT(COLOR_VALUE_G) : CVT100_OUT(COLOR_VALUE_B);
|
|
color[2] = tokenize ? CVT100_OUT(COLOR_VALUE_G) : CVT100_OUT(COLOR_VALUE_B);
|
|
color[3] = use_cache ? CVT100_OUT(COLOR_VALUE_G) : CVT100_OUT(COLOR_VALUE_B);
|
|
ZYAN_PRINTF("Minimal-Mode %s%d%s, Format %s%d%s, Tokenize %s%d%s, Caching %s%d%s, " \
|
|
"Instructions: %s%6.2fM%s, Time: %s%8.2f%s msec\n",
|
|
color[0], minimal_mode, CVT100_OUT(COLOR_DEFAULT),
|
|
color[1], format, CVT100_OUT(COLOR_DEFAULT),
|
|
color[2], tokenize, CVT100_OUT(COLOR_DEFAULT),
|
|
color[3], use_cache, CVT100_OUT(COLOR_DEFAULT),
|
|
CVT100_OUT(COLOR_VALUE_B), (double)count / 1000000, CVT100_OUT(COLOR_DEFAULT),
|
|
CVT100_OUT(COLOR_VALUE_G), GetCounter(), CVT100_OUT(COLOR_DEFAULT));
|
|
}
|
|
|
|
static void GenerateTestData(FILE* file, ZyanU8 encoding)
|
|
{
|
|
ZydisDecoder decoder;
|
|
if (!ZYAN_SUCCESS(
|
|
ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64)))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sFailed to initialize decoder%s\n", CVT100_ERR(COLOR_ERROR),
|
|
CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ZyanU8 last = 0;
|
|
ZyanU32 count = 0;
|
|
ZydisDecodedInstruction instruction;
|
|
while (count < 100000)
|
|
{
|
|
ZyanU8 data[ZYDIS_MAX_INSTRUCTION_LENGTH];
|
|
for (int i = 0; i < ZYDIS_MAX_INSTRUCTION_LENGTH; ++i)
|
|
{
|
|
data[i] = rand() % 256;
|
|
}
|
|
const ZyanU8 offset = rand() % (ZYDIS_MAX_INSTRUCTION_LENGTH - 2);
|
|
switch (encoding)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
data[offset ] = 0x0F;
|
|
data[offset + 1] = 0x0F;
|
|
break;
|
|
case 2:
|
|
data[offset ] = 0x8F;
|
|
break;
|
|
case 3:
|
|
data[offset ] = 0xC4;
|
|
break;
|
|
case 4:
|
|
data[offset ] = 0xC5;
|
|
break;
|
|
case 5:
|
|
case 6:
|
|
data[offset ] = 0x62;
|
|
break;
|
|
default:
|
|
ZYAN_UNREACHABLE;
|
|
}
|
|
if (ZYAN_SUCCESS(ZydisDecoderDecodeInstruction(&decoder, ZYAN_NULL, data,
|
|
sizeof(data), &instruction)))
|
|
{
|
|
ZyanBool b = ZYAN_FALSE;
|
|
switch (encoding)
|
|
{
|
|
case 0:
|
|
b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_LEGACY);
|
|
break;
|
|
case 1:
|
|
b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_3DNOW);
|
|
break;
|
|
case 2:
|
|
b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_XOP);
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_VEX);
|
|
break;
|
|
case 5:
|
|
b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX);
|
|
break;
|
|
case 6:
|
|
b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX);
|
|
break;
|
|
default:
|
|
ZYAN_UNREACHABLE;
|
|
}
|
|
if (b)
|
|
{
|
|
fwrite(&data[0], sizeof(ZyanU8), instruction.length, file);
|
|
++count;
|
|
|
|
const ZyanU8 p = (ZyanU8)((double)count / 100000 * 100);
|
|
if (last < p)
|
|
{
|
|
last = p;
|
|
ZYAN_PRINTF("%3.0d%%\n", p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ============================================================================================== */
|
|
/* Entry point */
|
|
/* ============================================================================================== */
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
// Enable VT100 escape sequences on Windows, if the output is not redirected
|
|
g_vt100_stdout = (ZyanTerminalIsTTY(ZYAN_STDSTREAM_OUT) == ZYAN_STATUS_TRUE) &&
|
|
ZYAN_SUCCESS(ZyanTerminalEnableVT100(ZYAN_STDSTREAM_OUT));
|
|
g_vt100_stderr = (ZyanTerminalIsTTY(ZYAN_STDSTREAM_ERR) == ZYAN_STATUS_TRUE) &&
|
|
ZYAN_SUCCESS(ZyanTerminalEnableVT100(ZYAN_STDSTREAM_ERR));
|
|
|
|
if (ZydisGetVersion() != ZYDIS_VERSION)
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sInvalid zydis version%s\n",
|
|
CVT100_ERR(COLOR_ERROR), CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (argc < 3 || (ZYAN_STRCMP(argv[1], "-test") && ZYAN_STRCMP(argv[1], "-generate")))
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sUsage: %s -[test|generate] [directory]%s\n",
|
|
CVT100_ERR(COLOR_ERROR), (argc > 0 ? argv[0] : "PerfTest"),
|
|
CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
ZyanBool generate = ZYAN_FALSE;
|
|
if (!ZYAN_STRCMP(argv[1], "-generate"))
|
|
{
|
|
generate = ZYAN_TRUE;
|
|
}
|
|
const char* directory = argv[2];
|
|
|
|
static const struct
|
|
{
|
|
const char* encoding;
|
|
const char* filename;
|
|
} tests[7] =
|
|
{
|
|
{ "DEFAULT", "enc_default.dat" },
|
|
{ "3DNOW" , "enc_3dnow.dat" },
|
|
{ "XOP" , "enc_xop.dat" },
|
|
{ "VEX_C4" , "enc_vex_c4.dat" },
|
|
{ "VEX_C5" , "enc_vex_c5.dat" },
|
|
{ "EVEX" , "enc_evex.dat" },
|
|
{ "MVEX" , "enc_mvex.dat" }
|
|
};
|
|
|
|
if (generate)
|
|
{
|
|
time_t t;
|
|
srand((unsigned)time(&t));
|
|
} else
|
|
{
|
|
AdjustProcessAndThreadPriority();
|
|
}
|
|
|
|
for (ZyanU8 i = 0; i < ZYAN_ARRAY_LENGTH(tests); ++i)
|
|
{
|
|
FILE* file;
|
|
|
|
const ZyanUSize len = strlen(directory);
|
|
char buf[1024];
|
|
strncpy(&buf[0], directory, sizeof(buf) - 1);
|
|
if (generate)
|
|
{
|
|
file = fopen(strncat(buf, tests[i].filename, sizeof(buf) - len - 1), "wb");
|
|
} else
|
|
{
|
|
file = fopen(strncat(buf, tests[i].filename, sizeof(buf) - len - 1), "rb");
|
|
}
|
|
if (!file)
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR, "%sCould not open file \"%s\": %s%s\n",
|
|
CVT100_ERR(COLOR_ERROR), &buf[0], strerror(ZYAN_ERRNO),
|
|
CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
continue;
|
|
}
|
|
|
|
if (generate)
|
|
{
|
|
ZYAN_PRINTF("Generating %s%s%s ...\n", CVT100_OUT(COLOR_VALUE_B), tests[i].encoding,
|
|
CVT100_OUT(ZYAN_VT100SGR_RESET));
|
|
GenerateTestData(file, i);
|
|
} else
|
|
{
|
|
fseek(file, 0L, SEEK_END);
|
|
const long length = ftell(file);
|
|
void* buffer = malloc(length);
|
|
if (!buffer)
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR,
|
|
"%sFailed to allocate %" PRIu64 " bytes on the heap%s\n",
|
|
CVT100_ERR(COLOR_ERROR), (ZyanU64)length, CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
goto NextFile2;
|
|
}
|
|
|
|
rewind(file);
|
|
if (fread(buffer, 1, length, file) != (ZyanUSize)length)
|
|
{
|
|
ZYAN_FPRINTF(ZYAN_STDERR,
|
|
"%sCould not read %" PRIu64 " bytes from file \"%s\"%s\n",
|
|
CVT100_ERR(COLOR_ERROR), (ZyanU64)length, &buf[0],
|
|
CVT100_ERR(ZYAN_VT100SGR_RESET));
|
|
goto NextFile1;
|
|
}
|
|
|
|
ZYAN_PRINTF("%sTesting %s%s%s ...\n", CVT100_OUT(ZYAN_VT100SGR_FG_MAGENTA),
|
|
CVT100_OUT(ZYAN_VT100SGR_FG_BRIGHT_MAGENTA), tests[i].encoding,
|
|
CVT100_OUT(COLOR_DEFAULT));
|
|
TestPerformance(buffer, length, ZYAN_TRUE , ZYAN_FALSE, ZYAN_FALSE, ZYAN_FALSE);
|
|
TestPerformance(buffer, length, ZYAN_FALSE, ZYAN_FALSE, ZYAN_FALSE, ZYAN_FALSE);
|
|
// TestPerformance(buffer, length, ZYAN_FALSE, ZYAN_FALSE, ZYAN_FALSE, ZYAN_TRUE);
|
|
TestPerformance(buffer, length, ZYAN_FALSE, ZYAN_TRUE , ZYAN_FALSE, ZYAN_FALSE);
|
|
// TestPerformance(buffer, length, ZYAN_FALSE, ZYAN_TRUE , ZYAN_FALSE, ZYAN_TRUE);
|
|
TestPerformance(buffer, length, ZYAN_FALSE, ZYAN_TRUE , ZYAN_TRUE , ZYAN_FALSE);
|
|
// TestPerformance(buffer, length, ZYAN_FALSE, ZYAN_TRUE , ZYAN_TRUE , ZYAN_TRUE);
|
|
ZYAN_PUTS("");
|
|
|
|
NextFile1:
|
|
free(buffer);
|
|
}
|
|
|
|
NextFile2:
|
|
fclose(file);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ============================================================================================== */
|