/**
 * Copyright (c) 2021 OceanBase
 * OceanBase Database Proxy(ODP) 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_LIB_STRING_OB_FIXED_LENGTH_STRING_H_
#define OCEANBASE_LIB_STRING_OB_FIXED_LENGTH_STRING_H_

#include "lib/ob_define.h"
#include "lib/string/ob_string.h"
#include "lib/utility/ob_serialization_helper.h"
#include "lib/hash_func/murmur_hash.h"
namespace oceanbase
{
namespace common
{
// This class only used to encapsulate raw c_string, can't be inherited
template<int64_t N>
class ObFixedLengthString
{
  OB_UNIS_VERSION(1);

public:
  ObFixedLengthString();
  ObFixedLengthString(const char *str);
  ObFixedLengthString(const ObString &str);
  ObFixedLengthString(const ObFixedLengthString &str);
  ObFixedLengthString &operator =(const ObFixedLengthString &str);
  ~ObFixedLengthString() { }

  int assign(const char *str);
  int assign(const ObString &str);
  void reset() { buf_[0] = '\0'; }
  void reuse() { buf_[0] = '\0'; }
  // compare functions
  bool operator <(const ObFixedLengthString &str) const;
  bool operator >(const ObFixedLengthString &str) const;
  bool operator ==(const ObFixedLengthString &str) const;
  bool operator !=(const ObFixedLengthString &str) const;
  bool is_empty() const;

  const char *ptr() const { return buf_; }
  int64_t size() const { return strlen(buf_); }
  // dangerous api, invoker assure not to write more than N-1 bytes
  char *ptr() { return buf_; }

  uint64_t hash(uint64_t seed = 0) const;

  int64_t to_string(char *buf, const int64_t buf_len) const;

private:
  char buf_[N];
};

template<int64_t N>
ObFixedLengthString<N>::ObFixedLengthString()
{
  STATIC_ASSERT(N > 0, "N should greater than 0");
  buf_[0] = '\0';
}

template<int64_t N>
ObFixedLengthString<N>::ObFixedLengthString(const char *str)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(assign(str))) {
    LIB_LOG(WARN, "assign failed", K(str), K(ret));
  }
}

template<int64_t N>
ObFixedLengthString<N>::ObFixedLengthString(const ObString &str)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(assign(str))) {
    LIB_LOG(WARN, "assign failed", K(str), K(ret));
  }
}

template<int64_t N>
ObFixedLengthString<N>::ObFixedLengthString(const ObFixedLengthString &str)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(assign(str.buf_))) {
    LIB_LOG(WARN, "assign failed", K(str), K(ret));
  }
}

template<int64_t N>
ObFixedLengthString<N> &ObFixedLengthString<N>::operator =(
    const ObFixedLengthString &str)
{
  int ret = OB_SUCCESS;
  if (this != &str) {
    if (OB_FAIL(assign(str.buf_))) {
      LIB_LOG(WARN, "assign failed", K(str), K(ret));
    }
  }
  return *this;
}

template<int64_t N>
bool ObFixedLengthString<N>::operator <(const ObFixedLengthString &str) const
{
  return (STRCMP(buf_, str.buf_) < 0);
}

template<int64_t N>
bool ObFixedLengthString<N>::operator >(const ObFixedLengthString &str) const
{
  return (STRCMP(buf_, str.buf_) > 0);
}

template<int64_t N>
bool ObFixedLengthString<N>::operator ==(const ObFixedLengthString &str) const
{
  return (0 == STRCMP(buf_, str.buf_));
}

template<int64_t N>
bool ObFixedLengthString<N>::operator !=(const ObFixedLengthString &str) const
{
  return !(*this == str);
}

template<int64_t N>
bool ObFixedLengthString<N>::is_empty() const
{
  return ('\0' == buf_[0]);
}

template<int64_t N>
int ObFixedLengthString<N>::assign(const char *str)
{
  int ret = OB_SUCCESS;
  int nwrite = 0;
  if (NULL == str) {
    buf_[0] = '\0';
  } else if ((nwrite = snprintf(buf_, N, "%s", str)) >= N || nwrite < 0) {
    ret = OB_BUF_NOT_ENOUGH;
    LIB_LOG(WARN, "buf is not long enough, truncate",
        K(N), "str len", strlen(str), K(nwrite), K(ret));
    buf_[N-1] = '\0';
  }
  return ret;
}

template<int64_t N>
int ObFixedLengthString<N>::assign(const ObString &str)
{
  int ret = OB_SUCCESS;
  int nwrite = 0;
  if ((nwrite = snprintf(buf_, N, "%.*s", str.length(), str.ptr())) >= N || nwrite < 0) {
    ret = OB_BUF_NOT_ENOUGH;
    LIB_LOG(WARN, "buf is not long enough, truncate",
        K(N), "str len", str.length(), K(nwrite), K(ret));
    buf_[N-1] = '\0';
  }
  return ret;
}

template<int64_t N>
uint64_t ObFixedLengthString<N>::hash(uint64_t seed) const
{
  seed = murmurhash(buf_, static_cast<int32_t>(strlen(buf_)), seed);
  return seed;
}

template<int64_t N>
int64_t ObFixedLengthString<N>::to_string(char *buf, const int64_t buf_len) const
{
  int64_t pos = 0;
  pos = snprintf(buf, buf_len, "%s", buf_);
  if (pos < 0) {
    pos = 0;
  }
  return pos;
}

OB_SERIALIZE_MEMBER_TEMP(template<int64_t N>, ObFixedLengthString<N>, buf_);

}//end namespace common
}//end namespace oceanbase
#endif // OCEANBASE_SHARE_OB_FIXED_LENGTH_STRING_H_
