X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=util.c;h=ceed4491aa96c59d24a11b29be567d673b6f3a71;hp=19377b150ea109b2384a0bc3037f444d558e3d64;hb=9d89a059aa6d2a03d3a08de4db295e08e45458d9;hpb=759fca692121e736bb78dac77dcaed6919bf301c diff --git a/util.c b/util.c index 19377b1..ceed449 100644 --- a/util.c +++ b/util.c @@ -1,38 +1,7 @@ -/* - * Copyright (C) 2012, 2013 - * Dale Weiler - * Wolfgang Bumiller - * - * 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. - */ -#define GMQCC_PLATFORM_HEADER +#include +#include #include "gmqcc.h" -#include "platform.h" -/* - * Initially this was handled with a table in the gmqcc.h header, but - * much to my surprise the contents of the table was duplicated for - * each translation unit, causing all these strings to be duplicated - * for every .c file it was included into. This method culls back on - * it. This is a 'utility' function because the executor also depends - * on this for disassembled byte-code. - */ const char *util_instr_str[VINSTR_END] = { "DONE", "MUL_F", "MUL_V", "MUL_FV", "MUL_VF", "DIV_F", "ADD_F", "ADD_V", @@ -57,7 +26,7 @@ const char *util_instr_str[VINSTR_END] = { * only required if big endian .. otherwise no need to swap * data. */ -#if PLATFORM_BYTE_ORDER == GMQCC_BYTE_ORDER_BIG +#if PLATFORM_BYTE_ORDER == GMQCC_BYTE_ORDER_BIG || PLATFORM_BYTE_ORDER == -1 static GMQCC_INLINE void util_swap16(uint16_t *d, size_t l) { while (l--) { d[l] = (d[l] << 8) | (d[l] >> 8); @@ -76,76 +45,169 @@ const char *util_instr_str[VINSTR_END] = { * so let's go the safe way */ static GMQCC_INLINE void util_swap64(uint32_t *d, size_t l) { - /* while (l--) { uint64_t v; v = ((d[l] << 8) & 0xFF00FF00FF00FF00) | ((d[l] >> 8) & 0x00FF00FF00FF00FF); v = ((v << 16) & 0xFFFF0000FFFF0000) | ((v >> 16) & 0x0000FFFF0000FFFF); d[l] = (v << 32) | (v >> 32); } - */ - size_t i; - for (i = 0; i < l; i += 2) { - uint32_t v1 = d[i]; - d[i] = d[i+1]; - d[i+1] = v1; - util_swap32(d+i, 2); - } } #endif -void util_endianswap(void *_data, size_t length, unsigned int typesize) { +void util_endianswap(void *_data, size_t count, unsigned int typesize) { # if PLATFORM_BYTE_ORDER == -1 /* runtime check */ if (*((char*)&typesize)) return; #else - /* prevent unused warnings */ - (void) _data; - (void) length; - (void) typesize; # if PLATFORM_BYTE_ORDER == GMQCC_BYTE_ORDER_LITTLE + /* prevent unused warnings */ + (void) _data; + (void) count; + (void) typesize; return; # else switch (typesize) { case 1: return; case 2: - util_swap16((uint16_t*)_data, length>>1); + util_swap16((uint16_t*)_data, count); return; case 4: - util_swap32((uint32_t*)_data, length>>2); + util_swap32((uint32_t*)_data, count); return; case 8: - util_swap64((uint32_t*)_data, length>>3); + util_swap64((uint32_t*)_data, count); return; - default: exit(EXIT_FAILURE); /* please blow the fuck up! */ + default: + con_err ("util_endianswap: I don't know how to swap a %u byte structure!\n", typesize); + exit(EXIT_FAILURE); /* please blow the fuck up! */ } # endif #endif } +void util_swap_header(prog_header_t *code_header) { + util_endianswap(&code_header->version, 1, sizeof(code_header->version)); + util_endianswap(&code_header->crc16, 1, sizeof(code_header->crc16)); + util_endianswap(&code_header->statements.offset, 1, sizeof(code_header->statements.offset)); + util_endianswap(&code_header->statements.length, 1, sizeof(code_header->statements.length)); + util_endianswap(&code_header->defs.offset, 1, sizeof(code_header->defs.offset)); + util_endianswap(&code_header->defs.length, 1, sizeof(code_header->defs.length)); + util_endianswap(&code_header->fields.offset, 1, sizeof(code_header->fields.offset)); + util_endianswap(&code_header->fields.length, 1, sizeof(code_header->fields.length)); + util_endianswap(&code_header->functions.offset, 1, sizeof(code_header->functions.offset)); + util_endianswap(&code_header->functions.length, 1, sizeof(code_header->functions.length)); + util_endianswap(&code_header->strings.offset, 1, sizeof(code_header->strings.offset)); + util_endianswap(&code_header->strings.length, 1, sizeof(code_header->strings.length)); + util_endianswap(&code_header->globals.offset, 1, sizeof(code_header->globals.offset)); + util_endianswap(&code_header->globals.length, 1, sizeof(code_header->globals.length)); + util_endianswap(&code_header->entfield, 1, sizeof(code_header->entfield)); +} + +void util_swap_statements(prog_section_statement_t *statements) { + size_t i; + + for (i = 0; i < vec_size(statements); ++i) { + util_endianswap(&statements[i].opcode, 1, sizeof(statements[i].opcode)); + util_endianswap(&statements[i].o1, 1, sizeof(statements[i].o1)); + util_endianswap(&statements[i].o2, 1, sizeof(statements[i].o2)); + util_endianswap(&statements[i].o3, 1, sizeof(statements[i].o3)); + } +} + +void util_swap_defs_fields(prog_section_both_t *section) { + size_t i; + + for (i = 0; i < vec_size(section); ++i) { + util_endianswap(§ion[i].type, 1, sizeof(section[i].type)); + util_endianswap(§ion[i].offset, 1, sizeof(section[i].offset)); + util_endianswap(§ion[i].name, 1, sizeof(section[i].name)); + } +} + +void util_swap_functions(prog_section_function_t *functions) { + size_t i; + + for (i = 0; i < vec_size(functions); ++i) { + util_endianswap(&functions[i].entry, 1, sizeof(functions[i].entry)); + util_endianswap(&functions[i].firstlocal, 1, sizeof(functions[i].firstlocal)); + util_endianswap(&functions[i].locals, 1, sizeof(functions[i].locals)); + util_endianswap(&functions[i].profile, 1, sizeof(functions[i].profile)); + util_endianswap(&functions[i].name, 1, sizeof(functions[i].name)); + util_endianswap(&functions[i].file, 1, sizeof(functions[i].file)); + util_endianswap(&functions[i].nargs, 1, sizeof(functions[i].nargs)); + /* Don't swap argsize[] - it's just a byte array, which Quake uses only as such. */ + } +} + +void util_swap_globals(int32_t *globals) { + util_endianswap(globals, vec_size(globals), sizeof(int32_t)); +} + /* -* CRC algorithms vary in the width of the polynomial, the value of said polynomial, -* the initial value used for the register, weather the bits of each byte are reflected -* before being processed, weather the algorithm itself feeds input bytes through the -* register or XORs them with a byte from one end and then straight into the table, as -* well as (but not limited to the idea of reflected versions) where the final register -* value becomes reversed, and finally weather the value itself is used to XOR the final -* register value. AS such you can already imagine how painfully annoying CRCs are, -* of course we stand to target Quake, which expects it's certain set of rules for proper -* calculation of a CRC. +* Based On: +* Slicing-by-8 algorithms by Michael E. +* Kounavis and Frank L. Berry from Intel Corp. +* http://www.intel.com/technology/comms/perfnet/download/CRC_generators.pdf +* +* This code was made to be slightly less confusing with macros, which +* I suppose is somewhat ironic. +* +* The code had to be changed for non reflected on the output register +* since that's the method Quake uses. * -* In most traditional CRC algorithms on uses a reflected table driven method where a value -* or register is reflected if it's bits are swapped around it's center. For example: -* take the bits 0101 is the 4-bit reflection of 1010, and respectfully 0011 would be the -* reflection of 1100. Quake however expects a NON-Reflected CRC on the output, but still -* requires a final XOR on the values (0xFFFF and 0x0000) this is a standard CCITT CRC-16 -* which I respectfully as a programmer don't agree with. +* The code also had to be changed for CRC16, which is slightly harder +* since the CRC32 method in the original Intel paper used a different +* bit order convention. * -* So now you know what we target, and why we target it, despite how unsettling it may seem -* but those are what Quake seems to request. +* Notes about the table: +* - It's exactly 4K in size +* - 64 elements fit in a cache line +* - can do 8 iterations unrolled 8 times for free +* - The first 256 elements of the table are standard CRC16 table +* +* Table can be generated with the following utility: */ +#if 0 +#include +#include +int main(void) { + for (unsigned i = 0; i < 0x100; ++i) { + uint16_t x = i << 8; + for (int j = 0; j < 8; ++j) + x = (x << 1) ^ ((x & 0x8000) ? 0x1021 : 0); + tab[0][i] = x; + } + for (unsigned i = 0; i < 0x100; ++i) { + uint16_t c = tab[0][i]; + for (unsigned j = 1; j < 8; ++j) { + c = tab[0][c >> 8] ^ (c << 8); + tab[j][i] = c; + } + } + printf("static const uint16_t util_crc16_table[8][256] = {"); + for (int i = 0; i < 8; ++i) { + printf("{\n"); + for (int j = 0; j < 0x100; ++j) { + printf((j & 7) ? " " : " "); + printf((j != 0x100-1) ? "0x%04X," : "0x%04X", tab[i][j]); + if ((j & 7) == 7) + printf("\n"); + } + printf((i != 7) ? "}," : "}"); + } + printf("};\n"); + return 0; +} +#endif +/* + * Non-Reflective version is present as well as a reference. + * + * TODO: + * combine the crc16 into u32s and mask off low high for byte order + * to make the arrays smaller. + */ static const uint16_t util_crc16_table[8][256] = {{ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, @@ -414,11 +476,11 @@ static const uint16_t util_crc16_table[8][256] = {{ }}; /* Non - Reflected */ -uint16_t util_crc16(uint16_t current, const char *k, size_t len) { - register uint16_t h = current; +uint16_t util_crc16(uint16_t current, const char *GMQCC_RESTRICT k, size_t len) { + uint16_t h = current; /* don't load twice */ - uint8_t *GMQCC_RESTRICT data = (uint8_t *GMQCC_RESTRICT)k; + const uint8_t *GMQCC_RESTRICT data = (const uint8_t *GMQCC_RESTRICT)k; size_t n; /* deal with the first bytes as bytes until we reach an 8 byte boundary */ @@ -440,8 +502,12 @@ uint16_t util_crc16(uint16_t current, const char *k, size_t len) { SELECT_DATA(1) ^ SELECT_DATA(0); data += 8; + len -= 8; } + #undef SELECT_BULK + #undef SELECT_DATA + /* deal with the rest with the byte method */ for (n = len & 7; n; --n) h = (uint16_t)(h << 8) ^ (*util_crc16_table)[(h >> 8) ^ *data++]; @@ -453,7 +519,7 @@ uint16_t util_crc16(uint16_t current, const char *k, size_t len) { * modifier is the match to make and the transposition from it, while add is the upper-value that determines the * transposition from uppercase to lower case. */ -static GMQCC_INLINE size_t util_strtransform(const char *in, char *out, size_t outsz, const char *mod, int add) { +static size_t util_strtransform(const char *in, char *out, size_t outsz, const char *mod, int add) { size_t sz = 1; for (; *in && sz < outsz; ++in, ++out, ++sz) { *out = (*in == mod[0]) @@ -476,64 +542,144 @@ size_t util_optimizationtostr(const char *in, char *out, size_t outsz) { return util_strtransform(in, out, outsz, "_ ", 'a'-'A'); } +static int util_vasprintf(char **dat, const char *fmt, va_list args) { + int ret; + int len; + char *tmp = NULL; + char buf[128]; + va_list cpy; + + va_copy(cpy, args); + len = vsnprintf(buf, sizeof(buf), fmt, cpy); + va_end (cpy); + + if (len < 0) + return len; + + if (len < (int)sizeof(buf)) { + *dat = util_strdup(buf); + return len; + } + + tmp = (char*)mem_a(len + 1); + if ((ret = vsnprintf(tmp, len + 1, fmt, args)) != len) { + mem_d(tmp); + *dat = NULL; + return -1; + } + + *dat = tmp; + return len; +} + int util_snprintf(char *str, size_t size, const char *fmt, ...) { va_list arg; - int ret; - + int ret; va_start(arg, fmt); - ret = platform_vsnprintf(str, size, fmt, arg); + ret = vsnprintf(str, size, fmt, arg); va_end(arg); - return ret; } int util_asprintf(char **ret, const char *fmt, ...) { va_list args; - int read; - + int read; va_start(args, fmt); - read = platform_vasprintf(ret, fmt, args); - va_end (args); - + read = util_vasprintf(ret, fmt, args); + va_end(args); return read; } int util_sscanf(const char *str, const char *format, ...) { va_list args; - int read; - + int read; va_start(args, format); - read = platform_vsscanf(str, format, args); + read = vsscanf(str, format, args); va_end(args); - return read; } char *util_strncpy(char *dest, const char *src, size_t n) { - return platform_strncpy(dest, src, n); + return strncpy(dest, src, n); } + char *util_strncat(char *dest, const char *src, size_t n) { - return platform_strncat(dest, src, n); + return strncat(dest, src, n); } + char *util_strcat(char *dest, const char *src) { - return platform_strcat(dest, src); + return strcat(dest, src); } + const char *util_strerror(int err) { - return platform_strerror(err); + return strerror(err); } const struct tm *util_localtime(const time_t *timer) { - return platform_localtime(timer); + return localtime(timer); } + const char *util_ctime(const time_t *timer) { - return platform_ctime(timer); + return ctime(timer); } -bool util_isatty(fs_file_t *file) { - if (file == (fs_file_t*)stdout) return !!platform_isatty(STDOUT_FILENO); - if (file == (fs_file_t*)stderr) return !!platform_isatty(STDERR_FILENO); +int util_getline(char **lineptr, size_t *n, FILE *stream) { + int chr; + int ret; + char *pos; + + if (!lineptr || !n || !stream) + return -1; + if (!*lineptr) { + if (!(*lineptr = (char*)mem_a((*n=64)))) + return -1; + } + + chr = *n; + pos = *lineptr; + + for (;;) { + int c = getc(stream); + + if (chr < 2) { + *n += (*n > 16) ? *n : 64; + chr = *n + *lineptr - pos; + if (!(*lineptr = (char*)mem_r(*lineptr,*n))) + return -1; + pos = *n - chr + *lineptr; + } + + if (ferror(stream)) + return -1; + if (c == EOF) { + if (pos == *lineptr) + return -1; + else + break; + } + + *pos++ = c; + chr--; + if (c == '\n') + break; + } + *pos = '\0'; + return (ret = pos - *lineptr); +} + +#ifndef _WIN32 +#include +bool util_isatty(FILE *file) { + if (file == stdout) return !!isatty(STDOUT_FILENO); + if (file == stderr) return !!isatty(STDERR_FILENO); + return false; +} +#else +bool util_isatty(FILE *file) { return false; } +#endif + /* * A small noncryptographic PRNG based on: * http://burtleburtle.net/bob/rand/smallprng.html @@ -571,3 +717,16 @@ void util_seed(uint32_t value) { (void)util_rand(); } +size_t hash(const char *string) { + size_t hash = 0; + for(; *string; ++string) { + hash += *string; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + return hash; +} +