Skip to content

Instantly share code, notes, and snippets.

@AliceLR
Created August 4, 2021 11:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AliceLR/59260685a5ced32564fa9adb59966618 to your computer and use it in GitHub Desktop.
Save AliceLR/59260685a5ced32564fa9adb59966618 to your computer and use it in GitHub Desktop.
#if 0
[ -z "$PREFIX" ] && [ "$MSYSTEM" = "MINGW64" ] && { PREFIX=/mingw64; TARGET=filematch64; }
[ -z "$PREFIX" ] && [ "$MSYSTEM" = "MINGW32" ] && { PREFIX=/mingw32; TARGET=filematch32; }
[ -z "$PREFIX" ] && { PREFIX=/usr; }
[ -z "$TARGET" ] && { TARGET=filematch; }
echo "PREFIX=$PREFIX"
echo "TARGET=$TARGET"
g++ -O3 -Wall -Wextra -I"$PREFIX/include" filematch.cpp -o"$TARGET" -L"$PREFIX/lib" -lz
exit 0
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <chrono>
enum world_file_id
{
FILE_ID_NONE = 0x0000,
FILE_ID_WORLD_INFO = 0x0001, // properties file
FILE_ID_WORLD_GLOBAL_ROBOT = 0x0004, // properties file
FILE_ID_WORLD_SFX = 0x0007, // data, NUM_SFX * SFX_SIZE
FILE_ID_WORLD_CHARS = 0x0008, // data, 3584*15
FILE_ID_WORLD_PAL = 0x0009, // data, SMZX_PAL_SIZE * 3
FILE_ID_WORLD_PAL_INDEX = 0x000A, // data, 1024
FILE_ID_WORLD_VCO = 0x000C, // data
FILE_ID_WORLD_VCH = 0x000D, // data
FILE_ID_WORLD_PAL_INTENSITY = 0x000E, // data, SMZX_PAL_SIZE * 1
FILE_ID_WORLD_SPRITES = 0x0080, // properties file
FILE_ID_WORLD_COUNTERS = 0x0081, // counter format, use stream
FILE_ID_WORLD_STRINGS = 0x0082, // string format, use stream
FILE_ID_BOARD_INFO = 0x0100, // properties file (board_id)
FILE_ID_BOARD_BID = 0x0101, // data
FILE_ID_BOARD_BPR = 0x0102, // data
FILE_ID_BOARD_BCO = 0x0103, // data
FILE_ID_BOARD_UID = 0x0104, // data
FILE_ID_BOARD_UPR = 0x0105, // data
FILE_ID_BOARD_UCO = 0x0106, // data
FILE_ID_BOARD_OCH = 0x0107, // data
FILE_ID_BOARD_OCO = 0x0108, // data
FILE_ID_ROBOT = 0x1000, // prop. file (board_id + robot_id)
FILE_ID_SCROLL = 0x2000, // prop. file (board_id + robot_id)
FILE_ID_SENSOR = 0x3000 // prop. file (board_id + robot_id)
};
#define FILE_ID_MATCH(str) ((sizeof(str)-1 == len) && !strcasecmp(str, next))
#define FILE_ID_VALUE(...) FILE_ID_8(__VA_ARGS__,'\0','\0','\0','\0','\0','\0','\0','\0')
#define FILE_ID_8(a,b,c,d,e,f,g,h,...) \
(((uint64_t)a << 0) | \
((uint64_t)b << 8) | \
((uint64_t)c << 16) | \
((uint64_t)d << 24) | \
((uint64_t)e << 32) | \
((uint64_t)f << 40) | \
((uint64_t)g << 48) | \
((uint64_t)h << 56))
/**
* Since only ASCII values are ever valid IDs, double bit 6 to bit 5 as a
* cheap and tacky tolower replacement.
*/
#define FILE_ID_VALUE_TOLOWER(...) (FILE_ID_VALUE(__VA_ARGS__) | \
((FILE_ID_VALUE(__VA_ARGS__) & (uint64_t)0x4040404040404040) >> 1))
static inline uint64_t world_file_id_value(const char *filename, size_t len)
{
const char *f = filename;
switch(len)
{
case 1: return FILE_ID_VALUE_TOLOWER(f[0]);
case 2: return FILE_ID_VALUE_TOLOWER(f[0],f[1]);
case 3: return FILE_ID_VALUE_TOLOWER(f[0],f[1],f[2]);
case 4: return FILE_ID_VALUE_TOLOWER(f[0],f[1],f[2],f[3]);
case 5: return FILE_ID_VALUE_TOLOWER(f[0],f[1],f[2],f[3],f[4]);
case 6: return FILE_ID_VALUE_TOLOWER(f[0],f[1],f[2],f[3],f[4],f[5]);
case 7: return FILE_ID_VALUE_TOLOWER(f[0],f[1],f[2],f[3],f[4],f[5],f[6]);
// Currently no valid world format filenames with >=8 chars.
}
return 0;
}
static inline void file_id_board_uint64(const char *next, size_t len,
unsigned int *_file_id, unsigned int *_board_id, unsigned int *_robot_id)
{
unsigned int robot_id = 0;
char temp[3] = { 0 };
if(len < 3 || len > 7)
return;
temp[0] = next[1];
temp[1] = next[2];
*_board_id = strtoul(temp, NULL, 16);
next += 3;
len -= 3;
if(next[0])
{
if(next[0] == 'r')
{
robot_id = strtoul(next+1, NULL, 16);
if(robot_id != 0)
{
*_file_id = FILE_ID_ROBOT;
}
*_robot_id = robot_id;
}
else
if(next[0] == 's')
{
if(next[1] == 'c')
{
robot_id = strtoul(next+2, NULL, 16);
if(robot_id != 0)
{
*_file_id = FILE_ID_SCROLL;
}
}
else
if(next[1] == 'e')
{
robot_id = strtoul(next+2, NULL, 16);
if(robot_id != 0)
{
*_file_id = FILE_ID_SENSOR;
}
}
*_robot_id = robot_id;
}
else
switch(world_file_id_value(next, len))
{
case FILE_ID_VALUE('b','i','d'):
*_file_id = FILE_ID_BOARD_BID;
break;
case FILE_ID_VALUE('b','p','r'):
*_file_id = FILE_ID_BOARD_BPR;
break;
case FILE_ID_VALUE('b','c','o'):
*_file_id = FILE_ID_BOARD_BCO;
break;
case FILE_ID_VALUE('u','i','d'):
*_file_id = FILE_ID_BOARD_UID;
break;
case FILE_ID_VALUE('u','p','r'):
*_file_id = FILE_ID_BOARD_UPR;
break;
case FILE_ID_VALUE('u','c','o'):
*_file_id = FILE_ID_BOARD_UCO;
break;
case FILE_ID_VALUE('o','c','h'):
*_file_id = FILE_ID_BOARD_OCH;
break;
case FILE_ID_VALUE('o','c','o'):
*_file_id = FILE_ID_BOARD_OCO;
break;
}
}
else
{
*_file_id = FILE_ID_BOARD_INFO;
}
}
static uint32_t file_id_uint64(const char *filename, size_t len)
{
const char *next = filename;
unsigned int file_id = FILE_ID_NONE;
unsigned int board_id;
unsigned int robot_id;
if(next[0] == 'b')
{
file_id_board_uint64(next, len, &file_id, &board_id, &robot_id);
}
else
switch(world_file_id_value(next, len))
{
case FILE_ID_VALUE('w','o','r','l','d'):
file_id = FILE_ID_WORLD_INFO;
break;
case FILE_ID_VALUE('g','r'):
file_id = FILE_ID_WORLD_GLOBAL_ROBOT;
break;
case FILE_ID_VALUE('s','f','x'):
file_id = FILE_ID_WORLD_SFX;
break;
case FILE_ID_VALUE('c','h','a','r','s'):
file_id = FILE_ID_WORLD_CHARS;
break;
case FILE_ID_VALUE('p','a','l'):
file_id = FILE_ID_WORLD_PAL;
break;
case FILE_ID_VALUE('p','a','l','i','d','x'):
file_id = FILE_ID_WORLD_PAL_INDEX;
break;
case FILE_ID_VALUE('p','a','l','i','n','t'):
file_id = FILE_ID_WORLD_PAL_INTENSITY;
break;
case FILE_ID_VALUE('v','c','o'):
file_id = FILE_ID_WORLD_VCO;
break;
case FILE_ID_VALUE('v','c','h'):
file_id = FILE_ID_WORLD_VCH;
break;
case FILE_ID_VALUE('s','p','r'):
file_id = FILE_ID_WORLD_SPRITES;
break;
case FILE_ID_VALUE('c','o','u','n','t','e','r'):
file_id = FILE_ID_WORLD_COUNTERS;
break;
case FILE_ID_VALUE('s','t','r','i','n','g'):
file_id = FILE_ID_WORLD_STRINGS;
break;
}
return file_id;
}
static inline void file_id_board_cmp(const char *next, size_t len,
unsigned int *_file_id, unsigned int *_board_id, unsigned int *_robot_id)
{
unsigned int robot_id = 0;
char temp[3] = { 0 };
if(len < 3 || len > 7)
return;
temp[0] = next[1];
temp[1] = next[2];
*_board_id = strtoul(temp, NULL, 16);
next += 3;
len -= 3;
if(next[0])
{
if(next[0] == 'r')
{
robot_id = strtoul(next+1, NULL, 16);
if(robot_id != 0)
{
*_file_id = FILE_ID_ROBOT;
}
*_robot_id = robot_id;
}
else
if(next[0] == 's')
{
if(next[1] == 'c')
{
robot_id = strtoul(next+2, NULL, 16);
if(robot_id != 0)
{
*_file_id = FILE_ID_SCROLL;
}
}
else
if(next[1] == 'e')
{
robot_id = strtoul(next+2, NULL, 16);
if(robot_id != 0)
{
*_file_id = FILE_ID_SENSOR;
}
}
*_robot_id = robot_id;
}
else
if(FILE_ID_MATCH("bid"))
{
*_file_id = FILE_ID_BOARD_BID;
}
else
if(FILE_ID_MATCH("bpr"))
{
*_file_id = FILE_ID_BOARD_BPR;
}
else
if(FILE_ID_MATCH("bco"))
{
*_file_id = FILE_ID_BOARD_BCO;
}
else
if(FILE_ID_MATCH("uid"))
{
*_file_id = FILE_ID_BOARD_UID;
}
else
if(FILE_ID_MATCH("upr"))
{
*_file_id = FILE_ID_BOARD_UPR;
}
else
if(FILE_ID_MATCH("uco"))
{
*_file_id = FILE_ID_BOARD_UCO;
}
else
if(FILE_ID_MATCH("och"))
{
*_file_id = FILE_ID_BOARD_OCH;
}
else
if(FILE_ID_MATCH("oco"))
{
*_file_id = FILE_ID_BOARD_OCO;
}
}
else
{
*_file_id = FILE_ID_BOARD_INFO;
}
}
static uint32_t file_id_cmp(const char *filename, size_t len)
{
const char *next = filename;
unsigned int file_id = FILE_ID_NONE;
unsigned int board_id;
unsigned int robot_id;
if(next[0] == 'b')
{
file_id_board_cmp(next, len, &file_id, &board_id, &robot_id);
}
else
if(FILE_ID_MATCH("world"))
{
file_id = FILE_ID_WORLD_INFO;
}
else
if(FILE_ID_MATCH("gr"))
{
file_id = FILE_ID_WORLD_GLOBAL_ROBOT;
}
else
if(FILE_ID_MATCH("sfx"))
{
file_id = FILE_ID_WORLD_SFX;
}
else
if(FILE_ID_MATCH("chars"))
{
file_id = FILE_ID_WORLD_CHARS;
}
else
if(FILE_ID_MATCH("pal"))
{
file_id = FILE_ID_WORLD_PAL;
}
else
if(FILE_ID_MATCH("palidx"))
{
file_id = FILE_ID_WORLD_PAL_INDEX;
}
else
if(FILE_ID_MATCH("palint"))
{
file_id = FILE_ID_WORLD_PAL_INTENSITY;
}
else
if(FILE_ID_MATCH("vco"))
{
file_id = FILE_ID_WORLD_VCO;
}
else
if(FILE_ID_MATCH("vch"))
{
file_id = FILE_ID_WORLD_VCH;
}
else
if(FILE_ID_MATCH("spr"))
{
file_id = FILE_ID_WORLD_SPRITES;
}
else
if(FILE_ID_MATCH("counter"))
{
file_id = FILE_ID_WORLD_COUNTERS;
}
else
if(FILE_ID_MATCH("string"))
{
file_id = FILE_ID_WORLD_STRINGS;
}
return file_id;
}
struct file_id_data
{
const char *filename;
enum world_file_id expected;
};
static const struct file_id_data data[] =
{
// Valid cases.
{ "world", FILE_ID_WORLD_INFO },
{ "WORLD", FILE_ID_WORLD_INFO },
{ "World", FILE_ID_WORLD_INFO },
{ "gr", FILE_ID_WORLD_GLOBAL_ROBOT },
{ "sfx", FILE_ID_WORLD_SFX },
{ "chars", FILE_ID_WORLD_CHARS },
{ "pal", FILE_ID_WORLD_PAL },
{ "palidx", FILE_ID_WORLD_PAL_INDEX },
{ "vco", FILE_ID_WORLD_VCO },
{ "vch", FILE_ID_WORLD_VCH },
{ "palint", FILE_ID_WORLD_PAL_INTENSITY },
{ "spr", FILE_ID_WORLD_SPRITES },
{ "counter", FILE_ID_WORLD_COUNTERS },
{ "string", FILE_ID_WORLD_STRINGS },
{ "b00", FILE_ID_BOARD_INFO },
{ "b01bid", FILE_ID_BOARD_BID },
{ "bFFbpr", FILE_ID_BOARD_BPR },
{ "b7Abco", FILE_ID_BOARD_BCO },
{ "b12uid", FILE_ID_BOARD_UID },
{ "b99upr", FILE_ID_BOARD_UPR },
{ "bEEuco", FILE_ID_BOARD_UCO },
{ "bADoch", FILE_ID_BOARD_OCH },
{ "bBBoco", FILE_ID_BOARD_OCO },
{ "b59r01", FILE_ID_ROBOT },
{ "b0FscFF", FILE_ID_SCROLL },
{ "bF0seFF", FILE_ID_SENSOR },
// Not valid cases.
{ "", FILE_ID_NONE },
{ "worlddd", FILE_ID_NONE },
{ "puddle", FILE_ID_NONE },
{ "a", FILE_ID_NONE },
{ "bb", FILE_ID_NONE },
{ "ccc", FILE_ID_NONE },
{ "dddd", FILE_ID_NONE },
{ "eeeee", FILE_ID_NONE },
{ "ffffff", FILE_ID_NONE },
{ "ggggggg", FILE_ID_NONE },
{ "hhhhhhhh",FILE_ID_NONE },
{ "bFFbi", FILE_ID_NONE },
{ "countr", FILE_ID_NONE },
{ "bFFse001",FILE_ID_NONE },
{ "b00r\x11",FILE_ID_NONE },
};
static constexpr size_t REPEAT_TIMES = 1000000;
template<uint32_t (*TESTFUNC)(const char *, size_t)>
__attribute__((noinline))
static void do_test(const char *TESTNAME)
{
auto start_time = std::chrono::steady_clock::now();
for(size_t i = 0; i < REPEAT_TIMES; i++)
{
for(const file_id_data &d : data)
{
uint32_t result = TESTFUNC(d.filename, strlen(d.filename));
if(result != d.expected)
{
printf("ERROR: test for '%s' expects %u, got %u\n", d.filename, d.expected, result);
exit(1);
}
}
}
auto end_time = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
printf("%-8.8s : %.3fms\n", TESTNAME, duration.count() / 1000.0);
fflush(stdout);
}
int main()
{
do_test<file_id_cmp>("strcmp");
do_test<file_id_uint64>("uint64");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment