/*
 * 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 COMMON_CONTAINER_SLICE_H
#define COMMON_CONTAINER_SLICE_H

#include <cassert>
#include <cstddef>
#include <cstring>
#include <string>

#include "common/allocator/alloc_base.h"
#include "common/logger/elog.h"
#include "utils/errno_define.h"
#include "utils/util_define.h"

namespace common {

class Slice {
   public:
    // Create an empty slice.
    // Slice() : data_(""), size_(0) {}
    Slice() {
        data_ = nullptr;
        size_ = 0;
    }

    // Create a slice that refers to the contents of "s"
    // Slice(const std::string& s) : data_(s.data()), size_(s.size()) {}
    Slice(const std::string& s) {
        int len = s.size();
        data_ = (char*)malloc(len + 1);  // TODO: user ourself's mem_alloc()
        if (UNLIKELY(nullptr == data_)) {
            // log_err("malloc() failed.");
        }
        strcpy(data_, s.c_str());
        size_ = len;
    }

    // Create a slice that refers to s[0,strlen(s)-1]
    // Slice(const char* s) : data_(s), size_(strlen(s)) {}
    Slice(const char* s) {
        int len = strlen(s);
        data_ = (char*)malloc(len + 1);  // TODO: user ourself's mem_alloc()
        if (UNLIKELY(nullptr == data_)) {
            // log_err("malloc() failed.");
        }
        strcpy(data_, s);
        size_ = len;
    }

    ~Slice() {
        free(data_);  // TODO: user ourself's mem_free()
        data_ = nullptr;
        size_ = 0;
    }

    // Intentionally copyable.
    // Slice(const Slice&) = default;
    // Slice& operator=(const Slice&) = default;
    Slice(const Slice& other) {
        int len = other.size_;
        data_ = (char*)malloc(len + 1);  // TODO: user ourself's mem_alloc()
        if (UNLIKELY(nullptr == data_)) {
            // log_err("malloc() failed.");
        }
        strcpy(data_, other.data_);
        size_ = len;
    }
    Slice& operator=(const Slice& other) {
        if (this->data_ != nullptr) {
            free(data_);  // TODO: user ourself's mem_free()
            data_ = nullptr;
        }
        int len = other.size_;
        this->data_ =
            (char*)malloc(len + 1);  // TODO: user ourself's mem_alloc()
        if (UNLIKELY(nullptr == data_)) {
            // log_err("malloc() failed.");
        }
        strcpy(this->data_, other.data_);
        this->size_ = len;
        return *this;
    }

    // Return a pointer to the beginning of the referenced data
    char* data() const { return data_; }

    // Return the length (in bytes) of the referenced data
    size_t size() const { return size_; }

    // Return true iff the length of the referenced data is zero
    bool empty() const { return size_ == 0; }

    // Return the ith byte in the referenced data.
    char operator[](size_t n) const {
        ASSERT(n < size());
        return data_[n];
    }

    // Return a string that contains the copy of the referenced data.
    std::string to_string() const { return std::string(data_, size_); }

    friend bool operator==(const Slice& x, const Slice& y) {
        return ((x.size() == y.size()) &&
                (memcmp(x.data(), y.data(), x.size()) == 0));
    }

    friend bool operator!=(const Slice& x, const Slice& y) { return !(x == y); }

    // Three-way comparison.  Returns value:
    //   <  0 iff "*this" <  "b",
    //   == 0 iff "*this" == "b",
    //   >  0 iff "*this" >  "b"
    int compare(const Slice& b) const {
        const size_t min_len = (size_ < b.size_) ? size_ : b.size_;
        int r = memcmp(data_, b.data_, min_len);
        if (r == 0) {
            if (size_ < b.size_)
                r = -1;
            else if (size_ > b.size_)
                r = +1;
        }
        return r;
    }

   private:
    char* data_;
    size_t size_;
};

}  // end namespace common
#endif  // COMMON_CONTAINER_SLICE_H
