xref: /6.0.3/couchdb/src/snappy/snappy_nif.cc (revision ac67408c)
1/**
2 * Copyright 2011,  Filipe David Manana  <fdmanana@apache.org>
3 * Web:  http://github.com/fdmanana/snappy-erlang-nif
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 * use this file except in compliance with the License. You may obtain a copy of
7 * 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, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 **/
17
18#include <iostream>
19#include <cstring>
20
21#include "erl_nif_compat.h"
22#include "google-snappy/snappy.h"
23#include "google-snappy/snappy-sinksource.h"
24
25#ifdef OTP_R13B03
26#error OTP R13B03 not supported. Upgrade to R13B04 or later.
27#endif
28
29#ifdef __cplusplus
30#define BEGIN_C extern "C" {
31#define END_C }
32#else
33#define BEGIN_C
34#define END_C
35#endif
36
37#define SC_PTR(c) reinterpret_cast<char *>(c)
38
39class SnappyNifSink : public snappy::Sink
40{
41    public:
42        SnappyNifSink(ErlNifEnv* e);
43        ~SnappyNifSink();
44
45        void Append(const char* data, size_t n);
46        char* GetAppendBuffer(size_t len, char* scratch);
47        ErlNifBinary& getBin();
48
49    private:
50        ErlNifEnv* env;
51        ErlNifBinary bin;
52        size_t length;
53};
54
55SnappyNifSink::SnappyNifSink(ErlNifEnv* e) : env(e), length(0)
56{
57    if(!enif_alloc_binary_compat(env, 0, &bin)) {
58        env = NULL;
59        throw std::bad_alloc();
60    }
61}
62
63SnappyNifSink::~SnappyNifSink()
64{
65    if(env != NULL) {
66        enif_release_binary_compat(env, &bin);
67    }
68}
69
70void
71SnappyNifSink::Append(const char *data, size_t n)
72{
73    if(data != (SC_PTR(bin.data) + length)) {
74        memcpy(bin.data + length, data, n);
75    }
76    length += n;
77}
78
79char*
80SnappyNifSink::GetAppendBuffer(size_t len, char* scratch)
81{
82    size_t sz;
83
84    if((length + len) > bin.size) {
85        sz = (len * 4) < 8192 ? 8192 : (len * 4);
86
87        if(!enif_realloc_binary_compat(env, &bin, bin.size + sz)) {
88            throw std::bad_alloc();
89        }
90    }
91
92    return SC_PTR(bin.data) + length;
93}
94
95ErlNifBinary&
96SnappyNifSink::getBin()
97{
98    if(bin.size > length) {
99        if(!enif_realloc_binary_compat(env, &bin, length)) {
100            throw std::bad_alloc();
101        }
102    }
103    return bin;
104}
105
106
107BEGIN_C
108
109
110ERL_NIF_TERM
111make_atom(ErlNifEnv* env, const char* name)
112{
113    ERL_NIF_TERM ret;
114    if(enif_make_existing_atom_compat(env, name, &ret, ERL_NIF_LATIN1)) {
115        return ret;
116    }
117    return enif_make_atom(env, name);
118}
119
120
121ERL_NIF_TERM
122make_ok(ErlNifEnv* env, ERL_NIF_TERM mesg)
123{
124    ERL_NIF_TERM ok = make_atom(env, "ok");
125    return enif_make_tuple2(env, ok, mesg);
126}
127
128
129ERL_NIF_TERM
130make_error(ErlNifEnv* env, const char* mesg)
131{
132    ERL_NIF_TERM error = make_atom(env, "error");
133    return enif_make_tuple2(env, error, make_atom(env, mesg));
134}
135
136
137ERL_NIF_TERM
138snappy_compress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
139{
140    ErlNifBinary input;
141
142    if(!enif_inspect_iolist_as_binary(env, argv[0], &input)) {
143        return enif_make_badarg(env);
144    }
145
146    try {
147        snappy::ByteArraySource source(SC_PTR(input.data), input.size);
148        SnappyNifSink sink(env);
149        snappy::Compress(&source, &sink);
150        return make_ok(env, enif_make_binary(env, &sink.getBin()));
151    } catch(std::bad_alloc e) {
152        return make_error(env, "insufficient_memory");
153    } catch(...) {
154        return make_error(env, "unknown");
155    }
156}
157
158
159ERL_NIF_TERM
160snappy_decompress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
161{
162    ErlNifBinary bin;
163    ErlNifBinary ret;
164    size_t len;
165
166    if(!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
167        return enif_make_badarg(env);
168    }
169
170    try {
171        if(!snappy::GetUncompressedLength(SC_PTR(bin.data), bin.size, &len)) {
172            return make_error(env, "data_not_compressed");
173        }
174
175        if(!enif_alloc_binary_compat(env, len, &ret)) {
176            return make_error(env, "insufficient_memory");
177        }
178
179        if(!snappy::RawUncompress(SC_PTR(bin.data), bin.size,
180                                            SC_PTR(ret.data))) {
181            return make_error(env, "corrupted_data");
182        }
183
184        return make_ok(env, enif_make_binary(env, &ret));
185    } catch(...) {
186        return make_error(env, "unknown");
187    }
188}
189
190
191ERL_NIF_TERM
192snappy_uncompressed_length(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
193{
194    ErlNifBinary bin;
195    size_t len;
196
197    if(!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
198        return enif_make_badarg(env);
199    }
200
201    try {
202        if(!snappy::GetUncompressedLength(SC_PTR(bin.data), bin.size, &len)) {
203            return make_error(env, "data_not_compressed");
204        }
205        return make_ok(env, enif_make_ulong(env, len));
206    } catch(...) {
207        return make_error(env, "unknown");
208    }
209}
210
211
212ERL_NIF_TERM
213snappy_is_valid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
214{
215    ErlNifBinary bin;
216
217    if (!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
218        return enif_make_badarg(env);
219    }
220
221    try {
222        if(snappy::IsValidCompressedBuffer(SC_PTR(bin.data), bin.size)) {
223            return make_atom(env, "true");
224        } else {
225            return make_atom(env, "false");
226        }
227    } catch(...) {
228        return make_error(env, "unknown");
229    }
230}
231
232
233int
234on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
235{
236    return 0;
237}
238
239
240int
241on_reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
242{
243    return 0;
244}
245
246
247int
248on_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
249{
250    return 0;
251}
252
253
254static ErlNifFunc nif_functions[] = {
255    {"compress", 1, snappy_compress},
256    {"decompress", 1, snappy_decompress},
257    {"uncompressed_length", 1, snappy_uncompressed_length},
258    {"is_valid", 1, snappy_is_valid}
259};
260
261
262ERL_NIF_INIT(snappy, nif_functions, &on_load, &on_reload, &on_upgrade, NULL);
263
264
265END_C
266