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

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

#include "common/allocator/alloc_base.h"
#include "common/global.h"
#include "utils/util_define.h"

namespace common {
class ByteBuffer {
   public:
    ByteBuffer()
        : data_(nullptr),
          variable_type_len_(sizeof(uint32_t)),
          real_data_size_(0),
          reserved_size_(0) {}

    ~ByteBuffer() {
        if (data_ && (reserved_size_ > 0)) {
            mem_free(data_);
            data_ = nullptr;
            real_data_size_ = 0;
            reserved_size_ = 0;
        }
    }

    FORCE_INLINE void init(uint32_t size) {
        data_ = static_cast<char*>(mem_alloc(size, MOD_TSBLOCK));
        reserved_size_ = size;
    }

    FORCE_INLINE void reset() { real_data_size_ = 0; }

    FORCE_INLINE void extend_memory(uint32_t new_size) {
        ASSERT(new_size > reserved_size_);
        data_ = static_cast<char*>(mem_realloc(data_, new_size));
        reserved_size_ = new_size;
    }

    FORCE_INLINE void append_variable_value(const char* value, uint32_t len) {
        // dynamic growth
        if (UNLIKELY((real_data_size_ + len + variable_type_len_) >
                     reserved_size_)) {
            // extreme scenarios, when encountering very long string
            uint32_t growth_size =
                g_config_value_.tsblock_mem_inc_step_size_ > len
                    ? g_config_value_.tsblock_mem_inc_step_size_
                    : (len + 1);
            extend_memory(reserved_size_ + growth_size);
        }

        ASSERT(data_);
        // append len
        memcpy(&data_[real_data_size_], reinterpret_cast<char*>(&len),
               variable_type_len_);
        real_data_size_ += variable_type_len_;
        if (len > 0) {
            // append data
            memcpy(&data_[real_data_size_], value, len);
            real_data_size_ += len;
        }
    }

    FORCE_INLINE void append_fixed_value(const char* value, uint32_t len) {
        // dynamic growth
        if (UNLIKELY(real_data_size_ + len > reserved_size_)) {
            // extreme scenarios, when encountering very long string
            uint32_t growth_size =
                g_config_value_.tsblock_mem_inc_step_size_ > len
                    ? g_config_value_.tsblock_mem_inc_step_size_
                    : (len + 1);
            extend_memory(reserved_size_ + growth_size);
        }

        ASSERT(data_);
        memcpy(&data_[real_data_size_], value, len);
        real_data_size_ += len;
    }

    // for fixed len value
    FORCE_INLINE char* read(uint32_t offset, uint32_t len) {
        ASSERT((offset + len) <= real_data_size_);
        char* p = &data_[offset];
        return p;
    }

    // for variable len value
    FORCE_INLINE char* read(uint32_t offset, uint32_t* len) {
        uint32_t tmp;
        // Directly memcpy to avoid potential alignment issues when casting
        // int32_t array pointer
        std::memcpy(&tmp, data_ + offset, sizeof(tmp));
        *len = tmp;
        char* p = &data_[offset + variable_type_len_];
        return p;
    }

    FORCE_INLINE char* get_data() { return data_; }

    FORCE_INLINE uint32_t get_data_size() const { return real_data_size_; }

   private:
    char* data_;
    uint8_t variable_type_len_;
    uint32_t real_data_size_;
    uint32_t reserved_size_;  // malloc memory size from system
};

}  // namespace common
#endif  // COMMON_CONTAINER_BYTE_BUFFER_H