1//
2// MessagePack for C++ deflate buffer implementation
3//
4// Copyright (C) 2010-2013 FURUHASHI Sadayuki and KONDO Takatoshi
5//
6//    Distributed under the Boost Software License, Version 1.0.
7//    (See accompanying file LICENSE_1_0.txt or copy at
8//    http://www.boost.org/LICENSE_1_0.txt)
9//
10#ifndef MSGPACK_ZBUFFER_HPP
11#define MSGPACK_ZBUFFER_HPP
12
13#include "msgpack/versioning.hpp"
14
15#include <stdexcept>
16#include <zlib.h>
17
18#ifndef MSGPACK_ZBUFFER_RESERVE_SIZE
19#define MSGPACK_ZBUFFER_RESERVE_SIZE 512
20#endif
21
22#ifndef MSGPACK_ZBUFFER_INIT_SIZE
23#define MSGPACK_ZBUFFER_INIT_SIZE 8192
24#endif
25
26namespace msgpack {
27
28/// @cond
29MSGPACK_API_VERSION_NAMESPACE(v1) {
30/// @endcond
31
32class zbuffer {
33public:
34    zbuffer(int level = Z_DEFAULT_COMPRESSION,
35            size_t init_size = MSGPACK_ZBUFFER_INIT_SIZE)
36        : m_data(nullptr), m_init_size(init_size)
37    {
38        m_stream.zalloc = Z_NULL;
39        m_stream.zfree = Z_NULL;
40        m_stream.opaque = Z_NULL;
41        m_stream.next_out = Z_NULL;
42        m_stream.avail_out = 0;
43        if(deflateInit(&m_stream, level) != Z_OK) {
44            throw std::bad_alloc();
45        }
46    }
47
48    ~zbuffer()
49    {
50        deflateEnd(&m_stream);
51        ::free(m_data);
52    }
53
54public:
55    void write(const char* buf, size_t len)
56    {
57        m_stream.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(buf));
58        m_stream.avail_in = len;
59
60        while(m_stream.avail_in > 0) {
61            if(m_stream.avail_out < MSGPACK_ZBUFFER_RESERVE_SIZE) {
62                if(!expand()) {
63                    throw std::bad_alloc();
64                }
65            }
66
67            if(deflate(&m_stream, Z_NO_FLUSH) != Z_OK) {
68                throw std::bad_alloc();
69            }
70        }
71    }
72
73    char* flush()
74    {
75        while(true) {
76            switch(deflate(&m_stream, Z_FINISH)) {
77            case Z_STREAM_END:
78                return m_data;
79            case Z_OK:
80                if(!expand()) {
81                    throw std::bad_alloc();
82                }
83                break;
84            default:
85                throw std::bad_alloc();
86            }
87        }
88    }
89
90    char* data()
91    {
92        return m_data;
93    }
94
95    const char* data() const
96    {
97        return m_data;
98    }
99
100    size_t size() const
101    {
102        return reinterpret_cast<char*>(m_stream.next_out) - m_data;
103    }
104
105    void reset()
106    {
107        if(deflateReset(&m_stream) != Z_OK) {
108            throw std::bad_alloc();
109        }
110        reset_buffer();
111    }
112
113    void reset_buffer()
114    {
115        m_stream.avail_out += reinterpret_cast<char*>(m_stream.next_out) - m_data;
116        m_stream.next_out = reinterpret_cast<Bytef*>(m_data);
117    }
118
119    char* release_buffer()
120    {
121        char* tmp = m_data;
122        m_data = nullptr;
123        m_stream.next_out = nullptr;
124        m_stream.avail_out = 0;
125        return tmp;
126    }
127
128private:
129    bool expand()
130    {
131        size_t used = reinterpret_cast<char*>(m_stream.next_out) - m_data;
132        size_t csize = used + m_stream.avail_out;
133        size_t nsize = (csize == 0) ? m_init_size : csize * 2;
134
135        char* tmp = static_cast<char*>(::realloc(m_data, nsize));
136        if(tmp == nullptr) {
137            return false;
138        }
139
140        m_data = tmp;
141        m_stream.next_out  = reinterpret_cast<Bytef*>(tmp + used);
142        m_stream.avail_out = nsize - used;
143
144        return true;
145    }
146#if defined(MSGPACK_USE_CPP03)
147private:
148    zbuffer(const zbuffer&);
149    zbuffer& operator=(const zbuffer&);
150#else  // defined(MSGPACK_USE_CPP03)
151    zbuffer(const zbuffer&) = delete;
152    zbuffer& operator=(const zbuffer&) = delete;
153#endif // defined(MSGPACK_USE_CPP03)
154
155private:
156    z_stream m_stream;
157    char* m_data;
158    size_t m_init_size;
159};
160
161/// @cond
162}  // MSGPACK_API_VERSION_NAMESPACE(v1)
163/// @endcond
164
165}  // namespace msgpack
166
167#endif /* msgpack/zbuffer.hpp */
168