#ifndef _LSUP_CORE_H #define _LSUP_CORE_H #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG #define DEBUG_TEST 1 // GDB breakpoints. #include #define BREAKPOINT if (DEBUG_TEST) raise (SIGINT) #define TRACE(fmt, ...) \ do {\ fprintf(stderr, "%s:%d:%s(): " fmt "\n", \ __FILE__, __LINE__, __func__, __VA_ARGS__); \ } while (0) #else /* DEBUG */ #define DEBUG_TEST 0 #define BREAKPOINT #define TRACE(fmt, ...) #endif /* DEBUG */ #define STR "%s" #define LIKELY(x) __builtin_expect(!!(x), true) #define UNLIKELY(x) __builtin_expect(!!(x), false) // TODO Cross-platform ramdisk path. #define TMPDIR "/tmp" #define KLEN sizeof(LSUP_Key) #define DBL_KLEN sizeof(LSUP_DoubleKey) #define TRP_KLEN sizeof(LSUP_TripleKey) #define QUAD_KLEN sizeof(LSUP_QuadKey) # define UUIDSTR_SIZE 37 // Handy flags operations. #define SET_FLAG(n, f) ((n) |= (f)) #define CLR_FLAG(n, f) ((n) &= ~(f)) #define TGL_FLAG(n, f) ((n) ^= (f)) #define CHK_FLAG(n, f) ((n) & (f)) /* * * RETURN CODES * * */ /** * 0 is success, positive integers (>88800) are warnings, and negative integers * (<-88800) are errors. */ typedef enum { LSUP_OK = 0, LSUP_NOACTION = 88801, LSUP_NORESULT = 88802, LSUP_END = 88803, LSUP_ERROR = -88801, LSUP_PARSE_ERR = -88802, LSUP_VALUE_ERR = -88803, LSUP_TXN_ERR = -88804, LSUP_DB_ERR = -88805, LSUP_NOT_IMPL_ERR = -88806, LSUP_IO_ERR = -88807, LSUP_MEM_ERR = -88808, } LSUP_rc; typedef enum { LSUP_BOOL_UNION, LSUP_BOOL_SUBTRACTION, LSUP_BOOL_INTERSECTION, LSUP_BOOL_XOR, } LSUP_bool_op; typedef size_t LSUP_Key; typedef LSUP_Key LSUP_DoubleKey[2]; typedef LSUP_Key LSUP_TripleKey[3]; typedef LSUP_Key LSUP_QuadKey[4]; typedef char uuid_str_t[UUIDSTR_SIZE]; // Yes, a textbook mistake; but writing min and max for all int types is crazy. #define min(x, y) (x) < (y) ? (x) : (y) #define max(x, y) (x) > (y) ? (x) : (y) /** @brief Make recursive directories. * * from https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950 */ LSUP_rc mkdir_p(const char *path, mode_t mode); /** @brief Remove a directory recursively, as in Unix "rm -r". * * @param path[in] Path of directory to remove. */ LSUP_rc rm_r (const char *path); /** @brief Convert a Unicode (UTF-8) code point to 4-byte sequence. */ /* inline uint16_t utf8_to_bytes (uint16_t c) { unsigned char b[4] = {0}; if (c<0x80) *b++=c; else if (c<0x800) *b++=192+c/64, *b++=128+c%64; else if (c-0xd800u<0x800) goto error; else if (c<0x10000) *b++=224+c/4096, *b++=128+c/64%64, *b++=128+c%64; else if (c<0x110000) *b++=240+c/262144, *b++=128+c/4096%64, *b++=128+c/64%64, *b++=128+c%64; else return NULL; return (uint16_t)b; } */ /** * Encode a code point using UTF-8 * * @author Ondřej Hruška * @license MIT * https://gist.github.com/MightyPork/52eda3e5677b4b03524e40c9f0ab1da5 * * @param out - output buffer (min 5 characters), will be 0-terminated * @param utf - code point 0-0x10FFFF * @return number of bytes on success, 0 on failure (also produces U+FFFD, * which uses 3 bytes) */ inline int utf8_encode(const uint32_t utf, unsigned char *out) { if (utf <= 0x7F) { // Plain ASCII out[0] = (char) utf; out[1] = 0; return 1; } else if (utf <= 0x07FF) { // 2-byte unicode out[0] = (char) (((utf >> 6) & 0x1F) | 0xC0); out[1] = (char) (((utf >> 0) & 0x3F) | 0x80); out[2] = 0; return 2; } else if (utf <= 0xFFFF) { // 3-byte unicode out[0] = (char) (((utf >> 12) & 0x0F) | 0xE0); out[1] = (char) (((utf >> 6) & 0x3F) | 0x80); out[2] = (char) (((utf >> 0) & 0x3F) | 0x80); out[3] = 0; return 3; } else if (utf <= 0x10FFFF) { // 4-byte unicode out[0] = (char) (((utf >> 18) & 0x07) | 0xF0); out[1] = (char) (((utf >> 12) & 0x3F) | 0x80); out[2] = (char) (((utf >> 6) & 0x3F) | 0x80); out[3] = (char) (((utf >> 0) & 0x3F) | 0x80); out[4] = 0; return 4; } else { // error - use replacement character out[0] = (char) 0xEF; out[1] = (char) 0xBF; out[2] = (char) 0xBD; out[3] = 0; return 0; } } // Error handling via goto. #define CHECK(exp, rc, marker) (rc) = (exp); if ((rc) != LSUP_OK) goto marker // Jump if rc is negative (skip warnings). #define PCHECK(exp, rc, marker) (rc) = (exp); if ((rc) < LSUP_OK) goto marker // Return rc if it is of LSUP_rc type and is negative (=error) #define RCCK(exp) LSUP_rc _rc = (exp); if (_rc < 0) return _rc // Return NULL if it is of LSUP_rc type and is negative (=error) #define RCNL(exp) if((exp) < 0) return NULL #define MALLOC_GUARD(var, rc) do { \ (var) = malloc (sizeof *(var)); \ if (UNLIKELY (var == NULL)) return (rc); \ } while (0); #define CALLOC_GUARD(var, rc) do { \ (var) = calloc (1, sizeof *(var)); \ if (UNLIKELY (var == NULL)) return (rc); \ } while (0); /* #define MALLOC_GUARD_ME(var) MALLOC_GUARD((var), LSUP_MEM_ERR) \ #define CALLOC_GUARD_ME(var) CALLOC_GUARD((var), LSUP_MEM_ERR) \ #define MALLOC_GUARD_NL(var) MALLOC_GUARD((var), NULL) \ #define CALLOC_GUARD_NL(var) CALLOC_GUARD((var), NULL) \ */ #endif