#ifndef _LSUP_CORE_H #define _LSUP_CORE_H #ifndef NOCOLOR #define LOG_USE_COLOR #endif #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "xxhash.h" #define LSUP_VERSION "1.0a3" // Logging and debugging. #ifdef DEBUG // GDB breakpoints. #include #define BREAKPOINT raise (SIGINT) #else #define BREAKPOINT #endif #define LIKELY(x) __builtin_expect(!!(x), true) #define UNLIKELY(x) __builtin_expect(!!(x), false) #define LSUP_NS "urn:lsup:" /// Default LS namespace. // 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 /** @brief "NULL" triple, a value that is never user-provided. */ #define NULL_TRP {NULL_KEY, NULL_KEY, NULL_KEY} /* * * RETURN CODES * * */ /** * 0 is success, positive integers (>88800) are warnings, and negative integers * (>-88900) are errors. */ typedef int LSUP_rc; /** @brief Generic success return code. */ #define LSUP_OK 0 /** @brief No action taken. * * An attempt to create or update a resource was made, but the resource already * existed substantially in the same form, so no action took place. The caller * is expected to find the resource as though the action actually took place. * If this is returned from the iteration of multiple updates, it means that * none of the iterations produced a change in state. */ #define LSUP_NOACTION 88801 /** @brief No result yielded. * * A read operation returned no results. If an iterator is expected to be * created, it may be created empty. */ #define LSUP_NORESULT 88802 /** @brief Loop end. * * End of a loop was reached. This can be used in a while() or for() * loop as a terminating condition. */ #define LSUP_END 88803 /** @brief Conflict warning. * An attempt to create or update a resource was made, but the resource existed * with a different form or value. The caller should find the value of the * existing resource to be different than the one that was attempted to store. * If this is returned from the iteration of multiple updates, it means that * other resources in the loop may have changed state and the operation as a * whole completed successfully. */ #define LSUP_CONFLICT 88804 /* * NOTE When adding new warning codes, use a value larger than the last one * in the list. Also change LSUP_MAX_WARNING. */ #define LSUP_MIN_WARNING LSUP_NOACTION #define LSUP_MAX_WARNING LSUP_CONFLICT /** @brief Generic error return code. */ #define LSUP_ERROR -88899 /** @brief TODO */ #define LSUP_PARSE_ERR -88898 /** @brief TODO */ #define LSUP_VALUE_ERR -88897 /** @brief TODO */ #define LSUP_TXN_ERR -88896 /** @brief TODO */ #define LSUP_DB_ERR -88895 /** @brief TODO */ #define LSUP_NOT_IMPL_ERR -88894 /** @brief TODO */ #define LSUP_IO_ERR -88893 /** @brief TODO */ #define LSUP_MEM_ERR -88892 /** @brief Conflict error. * * A critical resource conflict happened and no resources were updated. If this * is returned from the iteration of multiple updates, it means that the * operation has been interrupted and any state change within the loop prior to * the error has been rolled back. */ #define LSUP_CONFLICT_ERR -88891 /** @brief TODO */ #define LSUP_ENV_ERR -88890 /* * NOTE When adding new error codes, use a value larger than the last one * in the list. Also change LSUP_MAX_ERROR. */ #define LSUP_MIN_ERROR LSUP_ERROR #define LSUP_MAX_ERROR LSUP_ENV_ERR // Hashing stuff. #ifndef LSUP_HASH_SEED /** @brief Seed used for all hashing. Compile-time configurable. */ #define LSUP_HASH_SEED 0 #endif #define LSUP_HASH32(buf, size, seed) XXH32 (buf, size, seed) #define LSUP_HASH64(buf, size, seed) XXH3_64bits_withSeed (buf, size, seed) #define LSUP_HASH128(buf, size, seed) XXH3_128bits_withSeed (buf, size, seed) // TODO Add 32-bit switch. #define LSUP_HASH(buf, size, seed) LSUP_HASH64 (buf, size, seed) extern char *warning_msg[], *error_msg[]; extern char *LSUP_root_path; typedef XXH32_hash_t LSUP_Hash32; typedef XXH64_hash_t LSUP_Hash64; typedef XXH128_hash_t LSUP_Hash128; // TODO Add 32-bit switch. typedef LSUP_Hash64 LSUP_Hash; 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]; /** @brief Make recursive directories. * * Modified 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 Return an error message for a return code. */ const char * LSUP_strerror (LSUP_rc rc); /** @brief Encode a code point using UTF-8. * * https://gist.github.com/MightyPork/52eda3e5677b4b03524e40c9f0ab1da5 * * @author Ondřej Hruška * * @license MIT * * @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; } } /** @brief Log an error or warning for return codes that are not LSUP_OK. * * Note that, if used outside of the other macros below, care must be taken * to pass it an actual return code rather than an expression, otherwise the * expression will be evaluated multiple times. */ #define LOG_RC(rc) do { \ if ((rc) < 0) log_error (LSUP_strerror (rc)); \ else if ((rc) > 0) log_debug (LSUP_strerror (rc)); \ } while (0); /// Error handling via goto. #define CHECK(exp, marker) do { \ LSUP_rc _rc = (exp); \ LOG_RC(_rc); \ if (UNLIKELY (_rc != LSUP_OK)) goto marker; \ } while (0); /// Jump if rc is negative (skip warnings). #define PCHECK(exp, marker) do { \ LSUP_rc _rc = (exp); \ LOG_RC(_rc); \ if (UNLIKELY (_rc < LSUP_OK)) goto marker; \ } while (0); /// Return rc if it is of LSUP_rc type and nonzero. #define RCCK(exp) do { \ LSUP_rc _rc = (exp); \ LOG_RC(_rc); \ if (UNLIKELY (_rc != LSUP_OK)) return _rc; \ } while (0); /// Return rc if it is of LSUP_rc type and negative (=error) #define PRCCK(exp) do { \ LSUP_rc _rc = (exp); \ LOG_RC(_rc); \ if (UNLIKELY (_rc < LSUP_OK)) return _rc; \ } while (0); /// Return NULL if RC is nonzero. #define RCNL(exp) do { \ LSUP_rc _rc = (exp); \ LOG_RC(_rc); \ if (UNLIKELY (_rc != LSUP_OK)) return NULL; \ } while (0); /// Return NULL if RC is negative (=error) #define PRCNL(exp) do { \ LSUP_rc _rc = (exp); \ LOG_RC(_rc); \ if (UNLIKELY (_rc < LSUP_OK)) return NULL; \ } while (0); /// Allocate one pointer with malloc and return rc if it fails. #define MALLOC_GUARD(var, rc) do { \ (var) = malloc (sizeof *(var)); \ if (UNLIKELY (var == NULL)) return (rc); \ } while (0); /// Allocate one pointer with calloc and return rc if it fails. #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 /* _LSUP_CORE_H */