Files
oceanbase/deps/oblib/src/lib/utility/utility.h

1281 lines
36 KiB
C++

/**
* Copyright (c) 2021 OceanBase
* OceanBase CE is licensed under Mulan PubL v2.
* You can use this software according to the terms and conditions of the Mulan PubL v2.
* You may obtain a copy of Mulan PubL v2 at:
* http://license.coscl.org.cn/MulanPubL-2.0
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PubL v2 for more details.
*/
#ifndef OCEANBASE_COMMON_UTILITY_H_
#define OCEANBASE_COMMON_UTILITY_H_
#include <arpa/inet.h>
#include "easy_define.h"
#include "io/easy_io_struct.h"
#include "lib/allocator/ob_allocator.h"
#include "lib/stat/ob_diagnose_info.h"
#include "lib/utility/ob_print_utils.h"
#include "lib/utility/ob_utility.h"
#include "lib/oblog/ob_trace_log.h"
#include "lib/container/ob_iarray.h"
#define FALSE_IT(stmt) ({ (stmt); false; })
#define OB_FALSE_IT(stmt) ({ (stmt); false; })
#define CPUID_STD_SSE4_2 0x00100000
#define ob_htonll(i) \
( \
(((uint64_t)i & 0x00000000000000ff) << 56) | \
(((uint64_t)i & 0x000000000000ff00) << 40) | \
(((uint64_t)i & 0x0000000000ff0000) << 24) | \
(((uint64_t)i & 0x00000000ff000000) << 8) | \
(((uint64_t)i & 0x000000ff00000000) >> 8) | \
(((uint64_t)i & 0x0000ff0000000000) >> 24) | \
(((uint64_t)i & 0x00ff000000000000) >> 40) | \
(((uint64_t)i & 0xff00000000000000) >> 56) \
)
#define DEFAULT_TIME_FORMAT "%Y-%m-%d %H:%M:%S"
#define ob_bswap_16(v) \
(uint16_t)( \
(((uint16_t)v & 0x00ff) << 8) | \
(((uint16_t)v & 0xff00) >> 8) \
)
namespace oceanbase
{
namespace common
{
class ObScanner;
class ObRowkey;
class ObNewRange;
class ObSqlString;
struct ObObj;
class ObObjParam;
class ObAddr;
const int64_t LBT_BUFFER_LENGTH = 1024;
char *parray(char *buf, int64_t len, int64_t *array, int size);
char *lbt();
char *lbt(char *buf, int32_t len);
char *lbt(void **addrs, int32_t size);
void hex_dump(const void *data, const int32_t size,
const bool char_type = true, const int32_t log_level = OB_LOG_LEVEL_DEBUG);
int32_t parse_string_to_int_array(const char *line,
const char del, int32_t *array, int32_t &size);
/**
* parse string like int:32 to ObObj
*/
bool is2n(int64_t input);
constexpr int64_t next_pow2(const int64_t x)
{
return x > 1LL ? (1ULL << (8 * sizeof(int64_t) - __builtin_clzll(x - 1))) : 1LL;
}
bool all_zero(const char *buffer, const int64_t size);
bool all_zero_small(const char *buffer, const int64_t size);
char *str_trim(char *str);
char *ltrim(char *str);
char *rtrim(char *str);
const char *inet_ntoa_s(char *buffer, size_t n, const uint64_t ipport);
const char *inet_ntoa_s(char *buffer, size_t n, const uint32_t ip);
const char *time2str(const int64_t time_s, const char *format = DEFAULT_TIME_FORMAT);
int escape_range_string(char *buffer, const int64_t length, int64_t &pos, const ObString &in);
int escape_enter_symbol(char *buffer, const int64_t length, int64_t &pos, const char *src);
int sql_append_hex_escape_str(const ObString &str, ObSqlString &sql);
inline int sql_append_hex_escape_str(const char *str, const int64_t len, ObSqlString &sql)
{
return sql_append_hex_escape_str(ObString(0, static_cast<int32_t>(len), str), sql);
}
int convert_comment_str(char *comment_str);
int mem_chunk_serialize(char *buf, int64_t len, int64_t &pos, const char *data, int64_t data_len);
int mem_chunk_deserialize(const char *buf, int64_t len, int64_t &pos, char *data, int64_t data_len,
int64_t &real_len);
inline int64_t min(const int64_t x, const int64_t y)
{
return x > y ? y : x;
}
inline int64_t min(const int32_t x, const int64_t y)
{
return x > y ? y : x;
}
inline int64_t min(const int64_t x, const int32_t y)
{
return x > y ? y : x;
}
inline int32_t min(const int32_t x, const int32_t y)
{
return x > y ? y : x;
}
inline uint64_t min(const uint64_t x, const uint64_t y)
{
return x > y ? y : x;
}
inline uint32_t min(const uint32_t x, const uint32_t y)
{
return x > y ? y : x;
}
template <class T>
void min(T, T) = delete;
inline int64_t max(const int64_t x, const int64_t y)
{
return x < y ? y : x;
}
inline int64_t max(const int64_t x, const int32_t y)
{
return x < y ? y : x;
}
inline int64_t max(const int32_t x, const int64_t y)
{
return x < y ? y : x;
}
inline uint64_t max(const uint64_t x, const uint64_t y)
{
return x < y ? y : x;
}
inline int32_t max(const int32_t x, const int32_t y)
{
return x < y ? y : x;
}
inline uint32_t max(const uint32_t x, const uint32_t y)
{
return x < y ? y : x;
}
inline uint8_t max(const uint8_t x, const uint8_t y)
{
return x < y ? y : x;
}
inline int16_t max(const int16_t x, const int16_t y)
{
return x < y ? y : x;
}
inline double max(const double x, const double y)
{
return x < y ? y : x;
}
template <class T>
void max(T, T) = delete;
template<oceanbase::common::ObWaitEventIds::ObWaitEventIdEnum event_id = oceanbase::common::ObWaitEventIds::DEFAULT_SLEEP>
inline void ob_usleep(const useconds_t v)
{
oceanbase::common::ObWaitEventGuard wait_guard(event_id, 0, (int64_t)v);
::usleep(v);
}
int get_double_expand_size(int64_t &new_size, const int64_t limit_size);
/**
* allocate new memory that twice larger to store %oldp
* @param oldp: old memory content.
* @param old_size: old memory size.
* @param limit_size: expand memory cannot beyond this limit.
* @param new_size: expanded memory size.
* @param allocator: memory allocator.
*/
template <typename T, typename Allocator>
int double_expand_storage(T *&oldp, const int64_t old_size,
const int64_t limit_size, int64_t &new_size, Allocator &allocator)
{
int ret = OB_SUCCESS;
new_size = old_size;
void *newp = NULL;
if (OB_SUCCESS != (ret = get_double_expand_size(new_size, limit_size))) {
} else if (NULL == (newp = allocator.alloc(new_size * sizeof(T)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
} else {
for (int64_t i = 0; i < old_size; ++i) {
reinterpret_cast<T *>(newp)[i] = oldp[i];
}
if (NULL != oldp) { allocator.free(reinterpret_cast<char *>(oldp)); }
oldp = reinterpret_cast<T *>(newp);
}
return ret;
}
template <typename T>
int double_expand_storage(T *&oldp, const int64_t old_size,
const int64_t limit_size, int64_t &new_size, const lib::ObLabel &label)
{
ObMalloc allocator;
allocator.set_label(label);
return double_expand_storage(oldp, old_size, limit_size, new_size, allocator);
}
extern bool str_isprint(const char *str, const int64_t length);
extern int replace_str(char *src_str, const int64_t src_str_buf_size,
const char *match_str, const char *replace_str);
bool ez2ob_addr(ObAddr &addr, easy_addr_t& ez);
inline const char *get_peer_ip(char *buffer, size_t n, easy_request_t *req)
{
static char mess[8] = "unknown";
if (OB_LIKELY(NULL != req
&& NULL != req->ms
&& NULL != req->ms->c)) {
return easy_inet_addr_to_str(&req->ms->c->addr, buffer, (int)n);
} else {
return mess;
}
}
inline const char *get_peer_ip(char *buffer, size_t n, easy_connection_t *c)
{
static char mess[8] = "unknown";
if (OB_LIKELY(NULL != c)) {
return easy_inet_addr_to_str(&c->addr, buffer, (int)n);
} else {
return mess;
}
}
inline int get_fd(const easy_request_t *req)
{
int fd = -1;
if (OB_LIKELY(NULL != req
&& NULL != req->ms
&& NULL != req->ms->c)) {
fd = req->ms->c->fd;
}
return fd;
}
inline void init_easy_buf(easy_buf_t *buf, char *data, easy_request_t *req, uint64_t size)
{
if (NULL != buf && NULL != data) {
buf->pos = data;
buf->last = data;
buf->end = data + size;
buf->cleanup = NULL;
if (NULL != req && NULL != req->ms) {
buf->args = req->ms->pool;
}
buf->flags = 0;
easy_list_init(&buf->node);
}
}
inline easy_addr_t get_easy_addr(easy_request_t *req)
{
static easy_addr_t empty = {0, 0, {0}, 0};
if (OB_LIKELY(NULL != req
&& NULL != req->ms
&& NULL != req->ms->c)) {
return req->ms->c->addr;
} else {
return empty;
}
}
inline int extract_int(const ObString &str, int n, int64_t &pos, int64_t &value)
{
int ret = OB_SUCCESS;
if (!str.ptr() || str.length() <= 0 || pos < 0 || pos >= str.length()) {
ret = OB_INVALID_ARGUMENT;
} else {
const char *cur_ptr = str.ptr() + pos;
const char *end_ptr = str.ptr() + str.length();
int scanned = 0;
int64_t result = 0;
int64_t cur_value = 0;
//skip non-numeric character
while (cur_ptr < end_ptr && (*cur_ptr > '9' || *cur_ptr < '0')) {
cur_ptr++;
}
if (cur_ptr >= end_ptr) {
ret = OB_ERR_UNEXPECTED;
} else {
n = n > 0 ? n : str.length();
while (cur_ptr < end_ptr && scanned < n && *cur_ptr <= '9' && *cur_ptr >= '0') {
cur_value = *cur_ptr - '0';
result = result * 10L + cur_value;
scanned++;
cur_ptr++;
}
if (scanned <= 0) {
ret = OB_ERR_UNEXPECTED;
} else {
pos = cur_ptr - str.ptr();
value = result;
}
}
}
return ret;
}
inline int extract_int_reverse(const ObString &str, int n, int64_t &pos, int64_t &value)
{
int ret = OB_SUCCESS;
if (!str.ptr() || str.length() <= 0 || pos < 0 || pos >= str.length()) {
ret = OB_INVALID_ARGUMENT;
} else {
const char *cur_ptr = str.ptr() + pos;
const char *end_ptr = str.ptr();
int scanned = 0;
int64_t result = 0;
int64_t multi_unit = 1;
int64_t cur_value = 0;
//skip non-numeric character
while (cur_ptr >= end_ptr && (*cur_ptr > '9' || *cur_ptr < '0')) {
cur_ptr--;
}
if (cur_ptr < end_ptr) {
ret = OB_ERR_UNEXPECTED;
} else {
n = n > 0 ? n : str.length();
while (cur_ptr >= end_ptr && scanned < n && *cur_ptr <= '9' && *cur_ptr >= '0') {
cur_value = *cur_ptr - '0';
result += cur_value * multi_unit;
multi_unit *= 10L;
scanned++;
cur_ptr--;
}
if (scanned <= 0) {
ret = OB_ERR_UNEXPECTED;
} else {
value = result;
pos = cur_ptr - str.ptr();
}
}
}
return ret;
}
inline int split_on(ObString &src, const char sep, ObIArray<ObString> &result)
{
int ret = OB_SUCCESS;
ObString str = src.split_on(sep);
while (OB_SUCC(ret) && !str.empty()) {
if (OB_FAIL(result.push_back(str))) {
LIB_LOG(WARN, "push back error", K(ret));
} else {
str = src.split_on(sep);
}
}
if (OB_SUCC(ret) && !src.empty()) {
ret = result.push_back(src);
}
return ret;
}
//TODO why need this function.Remeber there's one deep copy function
template <typename Allocator>
int deep_copy_ob_string(Allocator &allocator, const ObString &src, ObString &dst)
{
int ret = OB_SUCCESS;
char *ptr = NULL;
if (NULL == src.ptr() || 0 >= src.length()) {
dst.assign_ptr(NULL, 0);
} else if (NULL == (ptr = reinterpret_cast<char *>(allocator.alloc(src.length())))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
} else {
MEMCPY(ptr, src.ptr(), src.length());
dst.assign(ptr, src.length());
}
return ret;
}
int deep_copy_obj(ObIAllocator &allocator, const ObObj &src, ObObj &dst);
int deep_copy_objparam(ObIAllocator &allocator, const ObObjParam &src, ObObjParam &dst);
struct SeqLockGuard
{
explicit SeqLockGuard(volatile uint64_t &seq): seq_(seq)
{
uint64_t tmp_seq = 0;
do {
tmp_seq = seq_;
} while ((tmp_seq & 1) || !__sync_bool_compare_and_swap(&seq_, tmp_seq, tmp_seq + 1));
}
~SeqLockGuard()
{
__sync_synchronize();
seq_++;
__sync_synchronize();
}
volatile uint64_t &seq_;
};
struct OnceGuard
{
OnceGuard(volatile uint64_t &seq): seq_(seq), locked_(false)
{
}
~OnceGuard()
{
if (locked_) {
__sync_synchronize();
seq_++;
}
}
bool try_lock()
{
uint64_t cur_seq = 0;
locked_ = (0 == ((cur_seq = seq_) & 1)) &&
__sync_bool_compare_and_swap(&seq_, cur_seq, cur_seq + 1);
return locked_;
}
volatile uint64_t &seq_;
bool locked_;
};
struct CountReporter
{
CountReporter(const char *id, int64_t report_mod)
: id_(id), seq_lock_(0), report_mod_(report_mod),
count_(0), start_ts_(0),
last_report_count_(0), last_report_time_(0),
total_cost_time_(0), last_cost_time_(0)
{
}
~CountReporter()
{
if (last_report_count_ > 0) {
_OB_LOG(INFO, "%s=%ld", id_, count_);
}
}
bool has_reported() { return last_report_count_ > 0; }
void inc(const int64_t submit_time)
{
int64_t count = __sync_add_and_fetch(&count_, 1);
int64_t total_cost_time = __sync_add_and_fetch(&total_cost_time_, (::oceanbase::common::ObTimeUtility::fast_current_time() - submit_time));
if (0 == (count % report_mod_)) {
SeqLockGuard lock_guard(seq_lock_);
int64_t cur_ts = ::oceanbase::common::ObTimeUtility::fast_current_time();
_OB_LOG_RET(ERROR, OB_ERROR, "%s=%ld:%ld:%ld\n", id_, count,
1000000 * (count - last_report_count_) / (cur_ts - last_report_time_),
(total_cost_time - last_cost_time_)/report_mod_);
last_report_count_ = count;
last_report_time_ = cur_ts;
last_cost_time_ = total_cost_time;
}
}
const char *id_;
uint64_t seq_lock_ CACHE_ALIGNED;
int64_t report_mod_;
int64_t count_ CACHE_ALIGNED;
int64_t start_ts_;
int64_t last_report_count_;
int64_t last_report_time_;
int64_t total_cost_time_;
int64_t last_cost_time_;
};
inline int64_t get_cpu_num()
{
static int64_t cpu_num = sysconf(_SC_NPROCESSORS_ONLN);
return cpu_num;
}
inline int64_t get_cpu_id()
{
return sched_getcpu();
}
// ethernet speed: byte / second.
int get_ethernet_speed(const char *devname, int64_t &speed);
int get_ethernet_speed(const ObString &devname, int64_t &speed);
inline int64_t get_phy_mem_size()
{
static int64_t page_size = sysconf(_SC_PAGE_SIZE);
static int64_t phys_pages = sysconf(_SC_PHYS_PAGES);
return page_size * phys_pages;
}
int64_t get_level1_dcache_size();
int64_t get_level1_icache_size();
int64_t get_level2_cache_size();
int64_t get_level3_cache_size();
inline bool is_cpu_support_sse42()
{
#if defined (__x86_64__)
uint32_t data;
asm("cpuid"
: "=c"(data)
: "a"(1)
:);
return 0 != (data & CPUID_STD_SSE4_2);
#elif defined(__aarch64__)
return 0;
#else
#error arch unsupported
#endif
}
///@brief Whether s1 is equal to s2, ignoring case and regarding space between words as one blank.
///If equal, return true. Otherwise, return false.
///
///For example:
///s1:" show collation " s2:"show collation", return true
///s1:"sh ow collation" s2:"show collation", return false
///@param [in] s1 input of string1
///@param [in] s1_len length of string1
///@param [in] s2 input of string2
///@param [in] s2_len length of string2
///@return true s1 is equal to s2,ignoring case and space
///@return false s1 is not equal to s2, or input arguments are wrong
bool is_case_space_equal(const char *s1, int64_t s1_len, const char *s2, int64_t s2_len);
///@brief Whether s1 is equal to s2 in no more than N characters, ignoring case and regarding space behind a word as one blank.
///If equal, return true. Otherwise, return false.
///
///For example:
///s1:" set names hello" s2:"set names *", cmp_len:strlen(s2)-1 return true
///WARN:To deal SQL"set names *", s1:"set names" s2:"set names *", cmp_len:strlen(s2)-1 return false
///@param [in] s1 input of string1
///@param [in] s1_len length of string1
///@param [in] s2 input of string2
///@param [in] s2_len length of string2
///@param [in] cmp_len length to be compared
///@return true s1 is equal to s2 in no more than N characters,ignoring case and space
///@return false s1 is not equal to s2, or input arguments are wrong
bool is_n_case_space_equal(const char *s1, int64_t s1_len, const char *s2, int64_t s2_len,
int64_t cmp_len);
///@brief Whether str is equal to wild_str with wild charset.
/// wild_many '%', wild_one '_', wild_prefix '\'
///@param [in] str string to be compared
///@param [in] wild_str string to be compared with wild charset
///@param [in] str_is_pattern whether str is pattern.
// When grant privileges to a database_name, this should be true;
int wild_compare(const char *str, const char *wild_str, const bool str_is_pattern);
///@brief Same functionality as 'wild_compare' with input of 'const char *'.
int wild_compare(const ObString &str, const ObString &wild_str, const bool str_is_pattern);
///@brief Get the sort value.
/// The string wich is more specific has larger number. Each string has 8 bits. The number 128
///represents string without wild char. Others represent the position of the fist wild char.
///wild char
///param [in] count count of arguments
///param [in] ... strings of needed to compute sort value
uint64_t get_sort(uint count, ...);
///@brief Get the sort value.
///param [in] str string needed to compute sort value
uint64_t get_sort(const ObString &str);
bool prefix_match(const char *prefix, const char *str);
int str_cmp(const void *v1, const void *v2);
////////////////////////////////////////////////////////////////////////////////////////////////////
inline void bind_self_to_core(uint64_t id)
{
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(id, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}
inline void bind_core()
{
static uint64_t idx = 0;
bind_self_to_core(ATOMIC_FAA(&idx, 1));
}
/*
* Load file to string. We alloc one more char for C string terminate '\0',
* so it is safe to use %str.ptr() as C string.
*/
template <typename Allocator>
int load_file_to_string(const char *path, Allocator &allocator, ObString &str)
{
int ret = OB_SUCCESS;
struct stat st;
char *buf = NULL;
int fd = -1;
int64_t size = 0;
if (NULL == path || strlen(path) == 0) {
ret = OB_INVALID_ARGUMENT;
} else if ((fd = ::open(path, O_RDONLY)) < 0) {
_OB_LOG(WARN, "open file %s failed, errno %d", path, errno);
ret = OB_ERROR;
} else if (0 != ::fstat(fd, &st)) {
_OB_LOG(WARN, "fstat %s failed, errno %d", path, errno);
ret = OB_ERROR;
} else if (NULL == (buf = allocator.alloc(st.st_size + 1))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
} else if ((size = static_cast<int64_t>(::read(fd, buf, st.st_size))) < 0) {
_OB_LOG(WARN, "read %s failed, errno %d", path, errno);
ret = OB_ERROR;
} else {
buf[size] = '\0';
str.assign(buf, static_cast<int>(size));
}
if (fd >= 0) {
int tmp_ret = close(fd);
if (tmp_ret < 0) {
_OB_LOG(WARN, "close %s failed, errno %d", path, errno);
ret = (OB_SUCCESS == ret) ? tmp_ret : ret;
}
}
return ret;
}
/**
* copy C string safely
*
* @param dest destination buffer
* @param dest_buflen destination buffer size
* @param src source string
* @param src_len source string length
*
* @return error code
*/
inline int ob_cstrcopy(char *dest, int64_t dest_buflen, const char* src, int64_t src_len)
{
int ret = OB_SUCCESS;
if (dest_buflen <= src_len) {
COMMON_LOG(WARN, "buffer not enough", K(dest_buflen), K(src_len));
ret = OB_BUF_NOT_ENOUGH;
} else {
MEMCPY(dest, src, src_len);
dest[src_len] = '\0';
}
return ret;
}
inline int ob_cstrcopy(char *dest, int64_t dest_buflen, const ObString &src_str)
{
return ob_cstrcopy(dest, dest_buflen, src_str.ptr(), src_str.length());
}
const char* get_default_if();
int start_daemon(const char *pidfile);
int ob_alloc_printf(ObString &result, ObIAllocator &alloc, const char* fmt, va_list ap);
int ob_alloc_printf(ObString &result, ObIAllocator &alloc, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
//Simple to ltoa. Need ensure dst buffer has at least 22 bytes
static const int64_t OB_LTOA10_CHAR_LEN = 22;
char *ltoa10(int64_t val,char *dst,const bool is_signed);
int long_to_str10(int64_t val,char *dst, const int64_t buf_len, const bool is_signed, int64_t &length);
template <typename T>
bool has_exist_in_array(const ObIArray<T> &array, const T &var, int64_t *idx = NULL)
{
bool ret = false;
int64_t num = array.count();
for (int64_t i = 0; i < num; i++) {
if (var == array.at(i)) {
ret = true;
if (idx != NULL) {
*idx = i;
}
break;
}
}
return ret;
}
template <typename T>
bool has_exist_in_array(const T *array, const int64_t num, const T &var)
{
bool ret = false;
for (int64_t i = 0; i < num; i++) {
if (var == array[i]) {
ret = true;
break;
}
}
return ret;
}
template <typename T>
int add_var_to_array_no_dup(ObIArray<T> &array, const T &var, int64_t *idx = NULL)
{
int ret = OB_SUCCESS;
if (has_exist_in_array(array, var, idx)) {
//do nothing
} else if (OB_FAIL(array.push_back(var))) {
LIB_LOG(WARN, "Add var to array error", K(ret));
} else if (idx != NULL) {
*idx = array.count() - 1;
}
return ret;
}
template <typename T>
int append_array_no_dup(ObIArray<T> &dst, const ObIArray<T> &src)
{
int ret = OB_SUCCESS;
for (int64_t idx = 0; OB_SUCC(ret) && idx < src.count(); ++idx) {
const T &var = src.at(idx);
if (has_exist_in_array(dst, var)) {
//do nothing
} else if (OB_FAIL(dst.push_back(var))) {
LIB_LOG(WARN, "Add var to array error", K(ret));
} else { } //do nothing
}
return ret;
}
//size:array capacity, num: current count
template <typename T>
int add_var_to_array_no_dup(T *array, const int64_t size, int64_t &num, const T &var)
{
int ret = OB_SUCCESS;
if (num > size) {
ret = OB_SIZE_OVERFLOW;
LIB_LOG(WARN, "Num >= size", K(ret));
} else {
if (has_exist_in_array(array, num, var)) {
//do nothing
} else if (num >= size) {
ret = OB_SIZE_OVERFLOW;
LIB_LOG(WARN, "Size is not enough", K(ret));
} else {
array[num++] = var;
}
}
return ret;
}
template <typename T, int64_t N = 1>
class ObPtrGuard
{
public:
explicit ObPtrGuard(ObIAllocator &allocator) : ptr_(NULL), allocator_(allocator) {}
~ObPtrGuard()
{
if (NULL != ptr_) {
for (int64_t i = 0; i < N; i++) {
ptr_[i].~T();
}
allocator_.free(ptr_);
ptr_ = NULL;
}
}
int init()
{
int ret = OB_SUCCESS;
if (NULL != ptr_) {
ret = OB_INIT_TWICE;
LIB_LOG(WARN, "already inited", K(ret));
} else {
T *mem = static_cast<T *>(allocator_.alloc(sizeof(T) * N));
if (NULL == mem) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LIB_LOG(ERROR, "alloc memory failed", K(ret), "size", sizeof(T) * N);
} else {
for (int64_t i = 0; i < N; i++) {
new (&mem[i]) T();
}
ptr_ = mem;
}
}
return ret;
}
T *ptr() { return ptr_; }
private:
T *ptr_;
ObIAllocator &allocator_;
private:
DISALLOW_COPY_AND_ASSIGN(ObPtrGuard);
};
class ObTimeGuard
{
public:
explicit ObTimeGuard(const char *owner = "unknown", const int64_t warn_threshold = INT64_MAX)
{
need_record_log_ = oceanbase::lib::is_trace_log_enabled();
if (need_record_log_) {
start_ts_ = common::ObTimeUtility::fast_current_time();
last_ts_ = start_ts_;
click_count_ = 0;
warn_threshold_ = warn_threshold;
owner_ = owner;
memset(click_, 0, sizeof(click_));
memset(click_str_, 0, sizeof(click_str_));
}
}
void click(const char *mod = NULL)
{
if (need_record_log_) {
const int64_t cur_ts = common::ObTimeUtility::fast_current_time();
if (OB_LIKELY(click_count_ < MAX_CLICK_COUNT)) {
click_str_[click_count_] = mod;
click_[click_count_++] = (int32_t)(cur_ts - last_ts_);
last_ts_ = cur_ts;
}
}
}
~ObTimeGuard()
{
if (need_record_log_) {
if (OB_UNLIKELY(get_diff() >= warn_threshold_)) {
LIB_LOG_RET(WARN, OB_ERR_TOO_MUCH_TIME, "destruct", K(*this));
}
}
}
int64_t get_diff() const
{
return need_record_log_ ? common::ObTimeUtility::fast_current_time() - start_ts_ : 0;
}
int64_t to_string(char *buf, const int64_t buf_len) const;
DECLARE_TO_YSON_KV;
private:
static const int64_t MAX_CLICK_COUNT = 16;
private:
int64_t start_ts_;
int64_t last_ts_;
int64_t click_count_;
int64_t warn_threshold_;
const char *owner_;
int32_t click_[MAX_CLICK_COUNT];
bool need_record_log_;
const char *click_str_[MAX_CLICK_COUNT];
};
class ObSimpleTimeGuard
{
public:
explicit ObSimpleTimeGuard(const int64_t warn_threshold = INT64_MAX)
{
need_record_log_ = oceanbase::lib::is_trace_log_enabled();
if (need_record_log_) {
start_ts_ = common::ObTimeUtility::fast_current_time();
warn_threshold_ = warn_threshold;
}
}
void click()
{
if (need_record_log_) {
if (OB_UNLIKELY(get_diff() >= warn_threshold_)) {
LIB_LOG_RET(WARN, OB_ERR_TOO_MUCH_TIME, "click", K(*this), KCSTRING(lbt()));
}
}
}
~ObSimpleTimeGuard()
{
if (need_record_log_) {
if (OB_UNLIKELY(get_diff() >= warn_threshold_)) {
LIB_LOG_RET(WARN, OB_ERR_TOO_MUCH_TIME, "destruct", K(*this), KCSTRING(lbt()));
}
}
}
int64_t get_diff() const
{
return need_record_log_ ? common::ObTimeUtility::fast_current_time() - start_ts_ : 0;
}
TO_STRING_KV(K_(start_ts), K_(warn_threshold));
private:
int64_t start_ts_;
int64_t warn_threshold_;
bool need_record_log_;
};
class ObTimeInterval
{
public:
explicit ObTimeInterval(const int64_t interval, const bool first_reach = true)
: last_ts_(0), interval_(interval)
{
if (!first_reach) {
last_ts_ = common::ObTimeUtility::fast_current_time();
}
}
~ObTimeInterval() {}
void reset()
{
last_ts_ = 0;
}
bool reach() const
{
bool bool_ret = false;
const int64_t now = common::ObTimeUtility::fast_current_time();
if (now - last_ts_ > interval_) {
bool_ret = true;
last_ts_ = now;
}
return bool_ret;
}
private:
mutable int64_t last_ts_;
const int64_t interval_;
};
class ObBandwidthThrottle
{
public:
ObBandwidthThrottle();
~ObBandwidthThrottle();
int init(const int64_t rate, const char *comment = "unknown");
int set_rate(const int64_t rate);
int get_rate(int64_t &rate);
int limit_and_sleep(const int64_t bytes, const int64_t last_active_time, const int64_t max_idle_time, int64_t &sleep_us);
void destroy();
private:
int cal_limit(const int64_t bytes, int64_t &avaliable_timestamp);
static int do_sleep(const int64_t next_avaliable_ts, const int64_t last_active_time, const int64_t max_idle_time, int64_t &sleep_us);
private:
common::ObSpinLock lock_;
int64_t rate_; // bandwidth limit bytes/s.
int64_t next_avaliable_timestamp_;
int64_t unlimit_bytes_;
int64_t total_bytes_;
int64_t total_sleep_ms_;
int64_t last_printed_bytes_;
int64_t last_printed_sleep_ms_;
int64_t last_printed_ts_;
char comment_[OB_MAX_TASK_COMMENT_LENGTH];
bool inited_;
};
class ObInOutBandwidthThrottle
{
public:
ObInOutBandwidthThrottle();
~ObInOutBandwidthThrottle();
int init(const int64_t rate);
int set_rate(const int64_t rate);
int get_rate(int64_t &rate);
int limit_in_and_sleep(const int64_t bytes, const int64_t last_active_time, const int64_t max_idle_time);
int limit_out_and_sleep(const int64_t bytes, const int64_t last_active_time, const int64_t max_idle_time, int64_t *need_sleep_us = nullptr);
void destroy();
private:
ObBandwidthThrottle in_throttle_;
ObBandwidthThrottle out_throttle_;
};
template<bool, typename T> struct __has_assign__;
template <typename T>
struct __has_assign__<true, T>
{
typedef int (T::*Sign)(const T &);
typedef char yes[1];
typedef char no[2];
template <typename U, U>
struct type_check;
template <typename _1> static yes &chk(type_check<Sign, &_1::assign> *);
template <typename> static no &chk(...);
static bool const value = sizeof(chk<T>(0)) == sizeof(yes);
};
template <typename T>
struct __has_assign__<false, T>
{
static bool const value = false;
};
template <typename T>
inline int get_copy_assign_ret_wrap(T &dest, FalseType)
{
UNUSED(dest);
return OB_SUCCESS;
}
template <typename T>
inline int get_copy_assign_ret_wrap(T &dest, TrueType)
{
return dest.get_copy_assign_ret();
}
template <typename T>
inline int copy_assign_wrap(T &dest, const T &src, FalseType)
{
dest = src;
return get_copy_assign_ret_wrap(dest, BoolType<HAS_MEMBER(T, get_copy_assign_ret)>());
}
template <typename T>
inline int copy_assign_wrap(T &dest, const T &src, TrueType)
{
return dest.assign(src);
}
template <typename T>
inline void set_member_allocator_wrap(T &dest, common::ObIAllocator *alloc, FalseType)
{
UNUSED(dest);
UNUSED(alloc);
}
template <typename T>
inline void set_member_allocator_wrap(T &dest, common::ObIAllocator *alloc, TrueType)
{
(void)dest.set_allocator(alloc);
}
template <typename T>
inline void set_member_allocator(T &dest, common::ObIAllocator *alloc)
{
set_member_allocator_wrap(dest, alloc, BoolType<HAS_MEMBER(T, set_allocator)>());
}
template <typename T>
inline int construct_assign_wrap(T &dest, const T &src, TrueType)
{
new(&dest) T();
return dest.assign(src);
}
template <typename T>
inline int construct_assign_wrap(T &dest, const T &src, FalseType)
{
new(&dest) T(src);
return get_copy_assign_ret_wrap(dest, BoolType<HAS_MEMBER(T, get_copy_assign_ret)>());
}
// This function is used for copy assignment
// -If T has a member function int assign(const T &), call dest.assign(src),
// And take the return value of the assign function as the return value;
// -If T is class && without member function int assign(const T &), then:
// -If T has a member function get_copy_assign_ret(), call dest=src,
// And get the return value through dest.get_copy_assign_ret() function;
// -If T has no member function get_copy_assign_ret(), call dest=src,
// And return OB_SUCCESS.
template <typename T>
inline int copy_assign(T &dest, const T &src)
{
return copy_assign_wrap(dest, src, BoolType<__has_assign__<__is_class(T), T>::value>());
}
// This function is used for copy construction
// -If T has a member function int assign(const T &) and no get_copy_assign_ret(),
// Call new(&dest) T() and dest.assign(src), and use the return value of the assign function as the return value;
// -Otherwise, call new(&dest) T(src), and at the same time:
// -If T has a member function get_copy_assign_ret(), then
// Obtain the return value through the dest.get_copy_assign_ret() function;
// -If T has no member function get_copy_assign_ret(), it returns OB_SUCCESS.
template <typename T>
inline int construct_assign(T &dest, const T &src)
{
return construct_assign_wrap(dest, src,
BoolType<__has_assign__<__is_class(T), T>::value && !HAS_MEMBER(T, get_copy_assign_ret)>());
}
template <typename T>
inline int objects_copy_wrap(T *dest, const T *src, int64_t count, TrueType)
{
int ret = OB_SUCCESS;
if (NULL == dest || NULL == src || count <= 0) {
ret = OB_INVALID_ARGUMENT;
LIB_LOG(WARN, "invalid argument", K(dest), K(src), K(count), K(ret));
} else {
MEMCPY(dest, src, sizeof(T) * count);
}
return ret;
}
template <typename T>
inline int objects_copy_wrap(T *dest, const T *src, int64_t count, FalseType)
{
int ret = OB_SUCCESS;
if (NULL == dest || NULL == src || count <= 0) {
ret = OB_INVALID_ARGUMENT;
LIB_LOG(WARN, "invalid argument", K(dest), K(src), K(count), K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < count; ++i) {
ret = copy_assign(dest[i], src[i]);
}
}
return ret;
}
template <typename T>
inline int objects_copy(T *dest, const T *src, int64_t count)
{
return objects_copy_wrap(dest, src, count, BoolType<std::is_trivially_copyable<T>::value>());
}
class ObMiniStat
{
public:
class ObStatItem
{
public:
ObStatItem(const char *item, const int64_t stat_interval)
: item_(item), stat_interval_(stat_interval), last_ts_(0), stat_count_(0), lock_tag_(false) {}
~ObStatItem() {}
public:
void stat(const int64_t time_cost = 0)
{
const int64_t cur_ts = ::oceanbase::common::ObTimeUtility::fast_current_time();
const int64_t cur_stat_count = ATOMIC_AAF(&stat_count_, 1);
const int64_t cur_accum_time = ATOMIC_AAF(&accum_time_, time_cost);
if (ATOMIC_LOAD(&last_ts_) + stat_interval_ < cur_ts) {
if (ATOMIC_BCAS(&lock_tag_, false, true)) {
LIB_LOG(INFO, NULL == item_ ? "" : item_, K(cur_stat_count), K_(stat_interval), "avg cost", cur_accum_time / cur_stat_count, K(this));
(void)ATOMIC_SET(&last_ts_, cur_ts);
(void)ATOMIC_SET(&stat_count_, 0);
(void)ATOMIC_SET(&accum_time_, 0);
ATOMIC_BCAS(&lock_tag_, true, false);
}
}
}
private:
const char *const item_;
const int64_t stat_interval_;
int64_t last_ts_;
int64_t stat_count_;
int64_t accum_time_;
bool lock_tag_;
};
public:
static void stat(ObStatItem &item)
{
item.stat();
}
};
class ObIntWarp
{
public:
ObIntWarp() : v_(0) {}
explicit ObIntWarp(const uint64_t v) : v_(v) {}
~ObIntWarp() { reset(); }
void reset() { v_ = 0; }
uint64_t hash() const { return v_; }
uint64_t get_value() const { return v_; }
int compare(const ObIntWarp &other) const
{
int ret = 0;
if (v_ == other.v_) {
ret = 0;
} else if (v_ > other.v_) {
ret = 1;
} else {
ret = -1;
}
return ret;
}
bool operator==(const ObIntWarp &other) const
{
return 0 == compare(other);
}
bool operator!=(const ObIntWarp &other) const
{
return !operator==(other);
}
TO_STRING_KV(K_(v));
private:
uint64_t v_;
};
class ObTsWindows
{
public:
ObTsWindows() : start_(0), base_(0), end_(0) {}
~ObTsWindows() {}
public:
void set(const int64_t start, const int64_t base, const int64_t end)
{
start_ = start;
base_ = base;
end_ = end;
}
void reset()
{
start_ = 0;
base_ = 0;
end_ = 0;
}
bool contain(const int64_t ts) const
{
return ts >= start_ && ts <= end_;
}
int64_t get_start() const { return start_; }
int64_t get_left_size() const { return base_ - start_; }
int64_t get_end() const { return end_; }
public:
TO_STRING_KV(K_(start), K_(base), K_(end));
private:
int64_t start_;
int64_t base_;
int64_t end_;
};
void get_addr_by_proxy_sessid(const uint64_t session_id, ObAddr &addr);
const char *replica_type_to_str(const ObReplicaType &type);
int ob_atoll(const char *str, int64_t &res);
int ob_strtoll(const char *str, char *&endptr, int64_t &res);
int ob_strtoull(const char *str, char *&endptr, uint64_t &res);
/* 功能:根据localtime计算公式实现快速计算的方法, 替代系统函数localtime_r. 参考https://www.cnblogs.com/westfly/p/5139645.html , 调整计算逻辑, 加上时区偏移的考虑.
参数:
in: const time_t *unix_sec, 当前的时间戳(单位秒), 输入值
out: struct tm *result, 当前时间戳对应的可读时间localtime, 输出值
返回值:
*/
struct tm *ob_localtime(const time_t *unix_sec, struct tm *result);
/* 功能:根据日志时间戳特点实现最快速计算localtime. 实现逻辑: 首先检查这次输入时间戳是否和上一次相同(秒单位相同, 绝大部分连续日志秒单位都是相同的), 如果相同则直接使用上一次计算的localtime即可. 如果不相同, 就使用快速时间戳计算方法ob_localtime计算localtime.
参数:
in/out: time_t &cached_unix_sec, 缓存的上一次时间戳(秒单位)
in/out: struct tm &cached_localtime, 缓存的上一次时间戳对应的可读时间localtime
in: const time_t &input_unix_sec, 当前的时间戳(单位秒), 输入值
out: struct tm *output_localtime, 当前时间戳对应的可读时间localtime, 输出值
返回值:
*/
void ob_fast_localtime(time_t &cached_unix_sec, struct tm &cached_localtime,
const time_t &input_unix_sec, struct tm *output_localtime);
template <typename T>
void call_dtor(T *&ptr)
{
if (NULL != ptr) {
ptr->~T();
ptr = NULL;
}
}
// Function: Check whether the directory is empty, in particular, if the directory does not exist, is_empty returns true
// parameter:
// in: dir_name, directory name
// out: is_empty, whether the directory is empty
// return value:
// OB_INVALID_ARGUMENT dir_name is NULL
// OB_IO_ERROR Error executing system call
// OB_SUCCESS successfully executed
int is_dir_empty(const char *dirname, bool &is_empty);
} // end namespace common
} // end namespace oceanbase
#endif //OCEANBASE_COMMON_UTILITY_H_