/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * License); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
#ifndef UTILS_UTIL_DEFINE_H
#define UTILS_UTIL_DEFINE_H

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>

/* ======== unsued ======== */
#define UNUSED(v) ((void)(v))

/* ======== inline ======== */
#ifdef __GNUC__
#define FORCE_INLINE inline __attribute__((always_inline))
#else
#define FORCE_INLINE inline
#endif  // __GNUC__

#ifdef BUILD_FOR_SMALL_BINARY
#define INLINE FORCE_INLINE
#else
#define INLINE
#endif  // BUILD_FOR_SMALL_BINARY

/* ======== likely ======== */
#if defined(__GNUC__) && __GNUC__ >= 4
#define LIKELY(x) (__builtin_expect((x), 1))
#define UNLIKELY(x) (__builtin_expect((x), 0))
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#endif  // __GNUC__ >= 4

/* ======== nullptr ======== */
#if __cplusplus < 201103L
#ifndef nullptr
#define nullptr NULL
#endif
#define OVERRIDE
#else
#define OVERRIDE override
#endif  // __cplusplus < 201103L

/* ======== cache line ======== */
#ifndef CACHE_LINE_SIZE
#define CACHE_LINE_SIZE 64
#endif  // CACHE_LINE_SIZE

/* ======== assert ======== */
#ifdef NDEBUG
#define ASSERT(condition) ((void)0)
#else
#define ASSERT(condition) assert((condition))
#endif  // NDEBUG

/* ======== statis assert ======== */
/*
 * To be compatible with C++ before C++11,
 * @msg should be a single word (use -/_ to concat)
 * such as This_should_be_TRUE
 */
#if __cplusplus < 201103L
// TODO only define this when DEBUG
#define STATIC_ASSERT(cond, msg) \
    typedef char static_assertion_##msg[(cond) ? 1 : -1] __attribute__((unused))
#else
#define STATIC_ASSERT(cond, msg) static_assert((cond), #msg)
#endif  // __cplusplus < 201103L

/* ======== atomic operation ======== */
#define ATOMIC_FAA(val_addr, addv) \
    __atomic_fetch_add((val_addr), (addv), __ATOMIC_SEQ_CST)
#define ATOMIC_AAF(val_addr, addv) \
    __atomic_add_fetch((val_addr), (addv), __ATOMIC_SEQ_CST)
/*
 * It implements an atomic compare and exchange operation.
 * This compares the contents of *ptr with the contents of *expected.
 * - If equal, the operation is a reader-modify-writer operation that writes
 * desired into *ptr.
 * - If they are not equal, the operation is a reader and the current contents
 * of *ptr are written into *expected
 */
#define ATOMIC_CAS(val_addr, expected, desired)                            \
    __atomic_compare_exchange_n((val_addr), (expected), (desired),         \
                                /* weak = */ false,                        \
                                /* success_memorder = */ __ATOMIC_SEQ_CST, \
                                /* failure_memorder = */ __ATOMIC_SEQ_CST)
#define ATOMIC_LOAD(val_addr) __atomic_load_n((val_addr), __ATOMIC_SEQ_CST)
#define ATOMIC_STORE(val_addr, val) \
    __atomic_store_n((val_addr), (val), __ATOMIC_SEQ_CST)

/* ======== align ======== */
#define ALIGNED(a) __attribute__((aligned(a)))
#define ALIGNED_4 ALIGNED(4)
#define ALIGNED_8 ALIGNED(8)

/* ======== disallow copy and assign ======== */
#if __cplusplus < 201103L
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
    TypeName(const TypeName&);             \
    void operator=(const TypeName&)
#else
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
    TypeName(const TypeName&) = delete;    \
    TypeName& operator=(const TypeName&) = delete;
#endif

/* ======== return value check ======== */
#define RET_FAIL(expr) UNLIKELY(common::E_OK != (ret = (expr)))
#define RFAIL(expr) UNLIKELY(common::E_OK != (ret = (expr)))
#define RET_SUCC(expr) LIKELY(common::E_OK != (ret = (exprt)))
#define RSUCC(expr) LIKELY(common::E_OK != (ret = (exprt)))
#define IS_SUCC(ret) LIKELY(common::E_OK == (ret))
#define IS_FAIL(ret) UNLIKELY(common::E_OK != (ret))

#define IS_NULL(ptr) UNLIKELY((ptr) == nullptr)

/* ======== min/max ======== */
#define UTIL_MAX(a, b) ((a) > (b) ? (a) : (b))
#define UTIL_MIN(a, b) ((a) > (b) ? (b) : (a))

/*
 * int64_max < 10^20
 * consider +/- and the '\0' tail. 24 is enough
 */
#define INT64_TO_BASE10_MAX_LEN 24

#endif  // UTILS_UTIL_DEFINE_H
