summaryrefslogtreecommitdiff
path: root/src/snappy/c_src/snappy_nif.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/snappy/c_src/snappy_nif.cc')
-rw-r--r--src/snappy/c_src/snappy_nif.cc265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/snappy/c_src/snappy_nif.cc b/src/snappy/c_src/snappy_nif.cc
new file mode 100644
index 000000000..93c18595b
--- /dev/null
+++ b/src/snappy/c_src/snappy_nif.cc
@@ -0,0 +1,265 @@
+/**
+ * Copyright 2011, Filipe David Manana <fdmanana@apache.org>
+ * Web: http://github.com/fdmanana/snappy-erlang-nif
+ *
+ * Licensed 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.
+ **/
+
+#include <iostream>
+#include <cstring>
+
+#include "erl_nif_compat.h"
+#include "snappy/snappy.h"
+#include "snappy/snappy-sinksource.h"
+
+#ifdef OTP_R13B03
+#error OTP R13B03 not supported. Upgrade to R13B04 or later.
+#endif
+
+#ifdef __cplusplus
+#define BEGIN_C extern "C" {
+#define END_C }
+#else
+#define BEGIN_C
+#define END_C
+#endif
+
+#define SC_PTR(c) reinterpret_cast<char *>(c)
+
+class SnappyNifSink : public snappy::Sink
+{
+ public:
+ SnappyNifSink(ErlNifEnv* e);
+ ~SnappyNifSink();
+
+ void Append(const char* data, size_t n);
+ char* GetAppendBuffer(size_t len, char* scratch);
+ ErlNifBinary& getBin();
+
+ private:
+ ErlNifEnv* env;
+ ErlNifBinary bin;
+ size_t length;
+};
+
+SnappyNifSink::SnappyNifSink(ErlNifEnv* e) : env(e), length(0)
+{
+ if(!enif_alloc_binary_compat(env, 0, &bin)) {
+ env = NULL;
+ throw std::bad_alloc();
+ }
+}
+
+SnappyNifSink::~SnappyNifSink()
+{
+ if(env != NULL) {
+ enif_release_binary_compat(env, &bin);
+ }
+}
+
+void
+SnappyNifSink::Append(const char *data, size_t n)
+{
+ if(data != (SC_PTR(bin.data) + length)) {
+ memcpy(bin.data + length, data, n);
+ }
+ length += n;
+}
+
+char*
+SnappyNifSink::GetAppendBuffer(size_t len, char* scratch)
+{
+ size_t sz;
+
+ if((length + len) > bin.size) {
+ sz = (len * 4) < 8192 ? 8192 : (len * 4);
+
+ if(!enif_realloc_binary_compat(env, &bin, bin.size + sz)) {
+ throw std::bad_alloc();
+ }
+ }
+
+ return SC_PTR(bin.data) + length;
+}
+
+ErlNifBinary&
+SnappyNifSink::getBin()
+{
+ if(bin.size > length) {
+ if(!enif_realloc_binary_compat(env, &bin, length)) {
+ throw std::bad_alloc();
+ }
+ }
+ return bin;
+}
+
+
+static inline ERL_NIF_TERM
+make_atom(ErlNifEnv* env, const char* name)
+{
+ ERL_NIF_TERM ret;
+ if(enif_make_existing_atom_compat(env, name, &ret, ERL_NIF_LATIN1)) {
+ return ret;
+ }
+ return enif_make_atom(env, name);
+}
+
+
+static inline ERL_NIF_TERM
+make_ok(ErlNifEnv* env, ERL_NIF_TERM mesg)
+{
+ ERL_NIF_TERM ok = make_atom(env, "ok");
+ return enif_make_tuple2(env, ok, mesg);
+}
+
+
+static inline ERL_NIF_TERM
+make_error(ErlNifEnv* env, const char* mesg)
+{
+ ERL_NIF_TERM error = make_atom(env, "error");
+ return enif_make_tuple2(env, error, make_atom(env, mesg));
+}
+
+
+BEGIN_C
+
+
+ERL_NIF_TERM
+snappy_compress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary input;
+
+ if(!enif_inspect_iolist_as_binary(env, argv[0], &input)) {
+ return enif_make_badarg(env);
+ }
+
+ try {
+ snappy::ByteArraySource source(SC_PTR(input.data), input.size);
+ SnappyNifSink sink(env);
+ snappy::Compress(&source, &sink);
+ return make_ok(env, enif_make_binary(env, &sink.getBin()));
+ } catch(std::bad_alloc e) {
+ return make_error(env, "insufficient_memory");
+ } catch(...) {
+ return make_error(env, "unknown");
+ }
+}
+
+
+ERL_NIF_TERM
+snappy_decompress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary bin;
+ ErlNifBinary ret;
+ size_t len;
+
+ if(!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
+ return enif_make_badarg(env);
+ }
+
+ try {
+ if(!snappy::GetUncompressedLength(SC_PTR(bin.data), bin.size, &len)) {
+ return make_error(env, "data_not_compressed");
+ }
+
+ if(!enif_alloc_binary_compat(env, len, &ret)) {
+ return make_error(env, "insufficient_memory");
+ }
+
+ if(!snappy::RawUncompress(SC_PTR(bin.data), bin.size,
+ SC_PTR(ret.data))) {
+ return make_error(env, "corrupted_data");
+ }
+
+ return make_ok(env, enif_make_binary(env, &ret));
+ } catch(...) {
+ return make_error(env, "unknown");
+ }
+}
+
+
+ERL_NIF_TERM
+snappy_uncompressed_length(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary bin;
+ size_t len;
+
+ if(!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
+ return enif_make_badarg(env);
+ }
+
+ try {
+ if(!snappy::GetUncompressedLength(SC_PTR(bin.data), bin.size, &len)) {
+ return make_error(env, "data_not_compressed");
+ }
+ return make_ok(env, enif_make_ulong(env, len));
+ } catch(...) {
+ return make_error(env, "unknown");
+ }
+}
+
+
+ERL_NIF_TERM
+snappy_is_valid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary bin;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
+ return enif_make_badarg(env);
+ }
+
+ try {
+ if(snappy::IsValidCompressedBuffer(SC_PTR(bin.data), bin.size)) {
+ return make_atom(env, "true");
+ } else {
+ return make_atom(env, "false");
+ }
+ } catch(...) {
+ return make_error(env, "unknown");
+ }
+}
+
+
+int
+on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
+{
+ return 0;
+}
+
+
+int
+on_reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
+{
+ return 0;
+}
+
+
+int
+on_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
+{
+ return 0;
+}
+
+
+static ErlNifFunc nif_functions[] = {
+ {"compress", 1, snappy_compress},
+ {"decompress", 1, snappy_decompress},
+ {"uncompressed_length", 1, snappy_uncompressed_length},
+ {"is_valid", 1, snappy_is_valid}
+};
+
+
+ERL_NIF_INIT(snappy, nif_functions, &on_load, &on_reload, &on_upgrade, NULL);
+
+
+END_C