1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2017 Couchbase, Inc.
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17#pragma once
18
19#include <platform/compress-visibility.h>
20#include <platform/compression/allocator.h>
21#include <platform/sized_buffer.h>
22#include <memory>
23
24namespace cb {
25namespace compression {
26
27/**
28 * The compression API needs to allocate the output buffer during
29 * compression / decmpression, and keep the size around.
30 *
31 * We've got code in our system which use new/delete for allocating
32 * buffers, and some old code which still use cb_malloc/cb_free. We
33 * can't unfortunately mix and mach new/cb_free etc, and we don't
34 * want to reallocate the memory after using this method. That's
35 * why the Buffer class handle both the new and malloc internally.
36 *
37 */
38class CBCOMPRESS_PUBLIC_API Buffer {
39public:
40    /**
41     * Initialize a new buffer which use the requested memory allocator
42     * for memory allocation.
43     */
44    explicit Buffer(Allocator alloc = Allocator{})
45        : allocator(alloc), memory(nullptr, FreeDeleter(allocator)) {
46    }
47
48    /**
49     * Resize the underlying buffer. If the buffer grows to a bigger size
50     * the content of the allocated memory is undefined.
51     *
52     * @param sz The new size for the buffer
53     * @throws std::bad_alloc if we failed to allocate memory
54     */
55    void resize(size_t sz) {
56        if (sz > capacity_) {
57            memory.reset(allocator.allocate(sz));
58            capacity_ = sz;
59        }
60        size_ = sz;
61    }
62
63    /**
64     * Get a pointer to the backing storage for the buffer. The data area
65     * is a continuous memory space size() bytes big.
66     */
67    char* data() {
68        return memory.get();
69    }
70
71    /**
72     * Get a pointer to the backing storage for the buffer. The data area
73     * is a continuous memory space size() bytes big.
74     */
75    const char* data() const {
76        return memory.get();
77    }
78
79    /**
80     * Release / detach / take ownership of the underlying buffer
81     *
82     * The caller is now responsible for freeing the memory by using
83     * the allocator's deallocate method
84     */
85    char* release() {
86        auto* ret = memory.release();
87        reset();
88        return ret;
89    }
90
91    /**
92     * Get the current size of the buffer
93     */
94    size_t size() const {
95        return size_;
96    }
97
98    /// Is this buffer empty or not
99    bool empty() const {
100        return size_ == 0;
101    }
102
103    /**
104     * Get the capacity of the buffer
105     */
106    size_t capacity() const {
107        return capacity_;
108    }
109
110    /**
111     * Reset the buffer (and free up all resources)
112     */
113    void reset() {
114        memory.reset();
115        capacity_ = size_ = 0;
116    }
117
118    /**
119     * Convenience operator to get a sized buffer pointing into the
120     * buffer owned by this buffer
121     */
122    operator cb::char_buffer() {
123        return cb::char_buffer{data(), size()};
124    }
125
126    /**
127     * Convenience operator to get a sized buffer pointing into the
128     * buffer owned by this buffer
129     */
130    operator cb::const_char_buffer() const {
131        return cb::const_char_buffer{data(), size()};
132    }
133
134    operator cb::const_byte_buffer() const {
135        return cb::const_byte_buffer{reinterpret_cast<const uint8_t*>(data()),
136                                     size()};
137    }
138
139    Allocator allocator;
140
141private:
142    size_t capacity_ = 0;
143    size_t size_ = 0;
144
145    struct FreeDeleter {
146        FreeDeleter() = delete;
147        explicit FreeDeleter(Allocator& alloc) : allocator(alloc) {
148        }
149        void operator()(char* ptr) {
150            allocator.deallocate(ptr);
151        }
152
153        Allocator& allocator;
154    };
155    std::unique_ptr<char, FreeDeleter> memory;
156};
157
158} // namespace compression
159} // namespace cb
160