1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef RINGBUFFER_H
17#define RINGBUFFER_H
18
19#include <mutex>
20#include <securec.h>
21
22class RingBuffer {
23public:
24    enum MemAlignShift:std::size_t {
25        B_ALIGN_SHIFT = 0,  // byte alingment
26        H_ALIGN_SHIFT = 1,  // half word alignment
27        W_ALIGN_SHIFT = 2,  // word alignment
28        D_ALIGN_SHIFT = 3,  // double word alignment
29    };
30
31    RingBuffer(
32        const std::size_t bufSize = DEFAULT_SIZE,
33        const enum MemAlignShift shift = B_ALIGN_SHIFT);
34
35    inline ~RingBuffer()
36    {
37        if (buffer_ != nullptr) {
38            delete[] buffer_;
39            buffer_ = nullptr;
40        }
41    }
42    inline operator bool()
43    {
44        std::lock_guard<std::mutex> lk {mtx_};
45        return buffer_;
46    }
47    inline std::size_t GetFreeSize()
48    {
49        std::lock_guard<std::mutex> lk {mtx_};
50        return FreeSize();
51    }
52    inline std::size_t GetDataSize()
53    {
54        std::lock_guard<std::mutex> lk {mtx_};
55        return DataSize();
56    }
57    inline std::size_t GetCapacity()
58    {
59        std::lock_guard<std::mutex> lk {mtx_};
60        return Capacity();
61    }
62    template<typename T>
63    int Peek(T *var)
64    {
65        const std::size_t len {sizeof(T)};
66        char *dest = reinterpret_cast<char*>(var);
67        if (dest == nullptr) {
68            return -1;
69        }
70        if (len == 0) {
71            return -1;
72        }
73        std::lock_guard<std::mutex> lk {mtx_};
74        auto dataSize = DataSize();
75        if (dataSize < len) {
76            return -1;
77        }
78        if (head_ + len > bufSize_) {
79            // data splitted
80            int ret = memcpy_s(dest, len, buffer_ + head_, bufSize_ - head_);
81            if (ret != EOK) {
82                return -1;
83            }
84            ret = memcpy_s(dest + bufSize_ - head_, len + head_ - bufSize_, buffer_, len + head_ - bufSize_);
85            if (ret != EOK) {
86                return -1;
87            }
88        } else {
89            if (memcpy_s(dest, len, buffer_ + head_, len) != EOK) {
90                return -1;
91            }
92        }
93        return 0;
94    }
95
96    ssize_t Read(const int fd, const std::size_t len);
97    ssize_t Write(const int fd, const std::size_t len);
98    std::size_t Get(char* dest, const std::size_t len);
99    int Put(const char* str, const std::size_t len);
100    int Put(const std::string& str);
101
102private:
103    inline std::size_t FreeSize() const
104    {
105        int res {0};
106        res = head_ - tail_;
107        if (res <= 0) {
108            res += bufSize_;
109        }
110        return static_cast<std::size_t>(res);
111    }
112    inline std::size_t DataSize(int noLock = 1) const
113    {
114        int res {0};
115        res = tail_ - head_;
116        if (res < 0) {
117            res += bufSize_;
118        }
119        return static_cast<std::size_t>(res);
120    }
121    inline std::size_t Capacity(int noLock = 1) const
122    {
123        return bufSize_;
124    }
125
126    int Resize();
127    char* Allocate(std::size_t bufSize);
128
129    enum BufferSize:std::size_t {
130        DEFAULT_SIZE = (1 << 8),
131    };
132    char *buffer_ {nullptr};
133    std::size_t bufSize_ {DEFAULT_SIZE};
134    const std::size_t alignShift_ {B_ALIGN_SHIFT};
135    /* head_ = tail_ mean empty, the buffer can never be full */
136    std::size_t head_ {0};   // first readable byte
137    std::size_t tail_ {0};          // first writebale byte
138    std::mutex mtx_;
139};
140#endif