couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dav...@apache.org
Subject [01/15] git commit: Initial commit
Date Mon, 04 Aug 2014 18:06:39 GMT
Repository: couchdb-b64url
Updated Branches:
  refs/heads/windsor-merge [created] 88eede873


Initial commit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-b64url/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-b64url/commit/13e21f49
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-b64url/tree/13e21f49
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-b64url/diff/13e21f49

Branch: refs/heads/windsor-merge
Commit: 13e21f49d4dee19255b345bca5bfaab1f29b38db
Parents: 
Author: Paul J. Davis <paul.joseph.davis@gmail.com>
Authored: Thu Oct 31 17:59:48 2013 -0500
Committer: Paul J. Davis <paul.joseph.davis@gmail.com>
Committed: Thu Oct 31 17:59:48 2013 -0500

----------------------------------------------------------------------
 .gitignore                       |   4 +
 c_src/couch_seqs_b64.c           | 409 ++++++++++++++++++++++++++++++++++
 rebar.config                     |  12 +
 src/couch_seqs.app.src           |   6 +
 src/couch_seqs_b64url.erl        |  72 ++++++
 test/couch_seqs_b64url_tests.erl |  35 +++
 6 files changed, 538 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-b64url/blob/13e21f49/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d1356fa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.eunit/
+c_src/*.o
+ebin/
+priv/*.so

http://git-wip-us.apache.org/repos/asf/couchdb-b64url/blob/13e21f49/c_src/couch_seqs_b64.c
----------------------------------------------------------------------
diff --git a/c_src/couch_seqs_b64.c b/c_src/couch_seqs_b64.c
new file mode 100644
index 0000000..8477508
--- /dev/null
+++ b/c_src/couch_seqs_b64.c
@@ -0,0 +1,409 @@
+
+#include <assert.h>
+#include <string.h>
+
+#include "erl_nif.h"
+
+
+typedef ERL_NIF_TERM ENTERM;
+
+
+typedef struct
+{
+    ENTERM atom_ok;
+    ENTERM atom_error;
+    ENTERM atom_partial;
+
+    ENTERM atom_nomem;
+
+    ErlNifResourceType* res_st;
+} cseq_priv;
+
+
+typedef struct
+{
+    ErlNifPid     pid;
+    ErlNifBinary* tgt;
+    size_t        len;
+    size_t        si;
+    size_t        ti;
+} cseq_st;
+
+
+typedef enum
+{
+    ST_OK,
+    ST_ERROR,
+    ST_PARTIAL
+} cseq_status;
+
+
+const char B64URL_B2A[256] = {
+   65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, //   0 -  15
+   81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99,100,101,102, //  16 -  31
+  103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118, //  32 -  47
+  119,120,121,122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 95, //  48 -  63
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, //  64 -  79
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, //  80 -  95
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, //  96 - 111
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 112 - 127
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 128 - 143
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 144 - 159
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 160 - 175
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 176 - 191
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 192 - 207
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 208 - 223
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 224 - 239
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255  // 240 - 255
+};
+
+const char B64URL_A2B[256] = {
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, //   0 -  15
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, //  16 -  31
+  255,255,255,255,255,255,255,255,255,255,255,255,255, 62,255,255, //  32 -  47
+   52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,255,255,255, //  48 -  63
+  255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, //  64 -  79
+   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255, 63, //  80 -  95
+  255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, //  96 - 111
+   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, // 112 - 127
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 128 - 143
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 144 - 159
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 160 - 175
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 176 - 191
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 192 - 207
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 208 - 223
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 224 - 239
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255  // 240 - 255
+};
+
+
+#define BYTES_PER_PERCENT 64
+
+static inline int
+do_consume_timeslice(ErlNifEnv* env) {
+#if(ERL_NIF_MAJOR_VERSION >= 2 && ERL_NIF_MINOR_VERSION >= 4)
+    return enif_consume_timeslice(env, 1);
+#else
+    return 0;
+#endif
+}
+
+
+static inline ENTERM
+make_atom(ErlNifEnv* env, const char* name)
+{
+    ENTERM ret;
+    if(enif_make_existing_atom(env, name, &ret, ERL_NIF_LATIN1)) {
+        return ret;
+    }
+    return enif_make_atom(env, name);
+}
+
+
+static inline ENTERM
+make_ok(ErlNifEnv* env, cseq_priv* priv, ENTERM value)
+{
+    return enif_make_tuple2(env, priv->atom_ok, value);
+}
+
+
+static inline ENTERM
+make_error(ErlNifEnv* env, cseq_priv* priv, ENTERM value)
+{
+    return enif_make_tuple2(env, priv->atom_error, value);
+}
+
+
+static inline ENTERM
+make_partial(ErlNifEnv* env, cseq_priv* priv, ENTERM value)
+{
+    return enif_make_tuple2(env, priv->atom_partial, value);
+}
+
+
+static inline int
+check_pid(ErlNifEnv* env, cseq_st* st)
+{
+    ErlNifPid self_pid;
+    ENTERM self;
+    ENTERM orig;
+
+    enif_self(env, &self_pid);
+    self = enif_make_pid(env, &self_pid);
+    orig = enif_make_pid(env, &(st->pid));
+
+    if(enif_compare(self, orig) == 0) {
+        return 1;
+    }
+
+    return 0;
+}
+
+
+static void
+cseq_free(ErlNifEnv* env, void* obj)
+{
+    cseq_st* st = (cseq_st*) obj;
+
+    if(st->tgt != NULL) {
+        enif_release_binary(st->tgt);
+        enif_free(st->tgt);
+    }
+}
+
+
+static int
+load(ErlNifEnv* env, void** priv, ENTERM info)
+{
+    int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER;
+    ErlNifResourceType* res;
+
+    cseq_priv* new_priv = (cseq_priv*) enif_alloc(sizeof(cseq_priv));
+    if(new_priv == NULL) {
+        return 1;
+    }
+
+    res = enif_open_resource_type(
+            env, NULL, "couch_seq", cseq_free, flags, NULL);
+    if(res == NULL) {
+        return 1;
+    }
+    new_priv->res_st = res;
+
+    new_priv->atom_ok = make_atom(env, "ok");
+    new_priv->atom_error = make_atom(env, "error");
+    new_priv->atom_partial = make_atom(env, "partial");
+
+    new_priv->atom_nomem = make_atom(env, "enomem");
+
+    *priv = (void*) new_priv;
+
+    return 0;
+}
+
+
+static int
+reload(ErlNifEnv* env, void** priv, ENTERM info)
+{
+    return 0;
+}
+
+
+static int
+upgrade(ErlNifEnv* env, void** priv, void** old_priv, ENTERM info)
+{
+    return 0;
+}
+
+
+static void
+unload(ErlNifEnv* env, void* priv)
+{
+    return;
+}
+
+
+static inline cseq_status
+cseq_b64url_encode(ErlNifEnv* env, ErlNifBinary* src, cseq_st* st)
+{
+    size_t chunk_start = st->si;
+    unsigned char c1;
+    unsigned char c2;
+    unsigned char c3;
+
+    assert(st->si % 3 == 0 && "invalid source index");
+    assert(st->ti % 4 == 0 && "invalid target index");
+
+    while(st->si + 3 <= src->size) {
+        c1 = src->data[st->si++];
+        c2 = src->data[st->si++];
+        c3 = src->data[st->si++];
+
+        st->tgt->data[st->ti++] = B64URL_B2A[(c1 >> 2) & 0x3F];
+        st->tgt->data[st->ti++] = B64URL_B2A[((c1 << 4) | (c2 >> 4))
& 0x3F];
+        st->tgt->data[st->ti++] = B64URL_B2A[((c2 << 2) | (c3 >> 6))
& 0x3F];
+        st->tgt->data[st->ti++] = B64URL_B2A[c3 & 0x3F];
+
+        if(st->si - chunk_start > BYTES_PER_PERCENT) {
+            if(do_consume_timeslice(env)) {
+                return ST_PARTIAL;
+            } else {
+                chunk_start = st->si;
+            }
+        }
+    }
+
+    if(src->size - st->si == 2) {
+        c1 = src->data[st->si];
+        st->tgt->data[st->ti++] = B64URL_B2A[(c1 >> 2) & 0x3F];
+        st->tgt->data[st->ti++] = B64URL_B2A[(c1 << 4) & 0x3F];
+    } else if(src->size - st->si == 1) {
+        c1 = src->data[st->si];
+        c2 = src->data[st->si+1];
+        st->tgt->data[st->ti++] = B64URL_B2A[(c1 >> 2) & 0x3F];
+        st->tgt->data[st->ti++] = B64URL_B2A[((c1 << 4) | (c2 >> 4))
& 0x3F];
+        st->tgt->data[st->ti++] = B64URL_B2A[(c2 << 2) & 0x3F];
+    } else {
+        assert(0 == 1 && "Inavlid length calculation");
+    }
+
+    return ST_OK;
+}
+
+
+static ENTERM
+cseq_b64url_encode_init(ErlNifEnv* env, int argc, const ENTERM argv[])
+{
+    ErlNifBinary src;
+    cseq_priv* priv = (cseq_priv*) enif_priv_data(env);
+    cseq_st* st = NULL;
+    size_t tlen;
+    size_t rem;
+    int status;
+    ENTERM ret;
+
+    if(argc != 1) {
+        return enif_make_badarg(env);
+    }
+
+    if(!enif_inspect_iolist_as_binary(env, argv[0], &src)) {
+        return enif_make_badarg(env);
+    }
+
+    if(src.size <= 0) {
+        return enif_make_badarg(env);
+    }
+
+    st = (cseq_st*) enif_alloc_resource(priv->res_st, sizeof(cseq_st));
+    if(st == NULL) {
+        ret = make_error(env, priv, priv->atom_nomem);
+        goto error;
+    }
+
+    memset(st, '\0', sizeof(cseq_st));
+    enif_self(env, &(st->pid));
+    st->len = src.size;
+    st->si = 0;
+    st->ti = 0;
+    st->tgt = (ErlNifBinary*) enif_alloc(sizeof(ErlNifBinary));
+    if(st->tgt == NULL) {
+        ret = make_error(env, priv, priv->atom_nomem);
+        goto error;
+    }
+
+    // The target length is defined as 4 * ceil(src_len/3) but we
+    // don't use '=' padding for URLs so we only add exactly the
+    // extra bytes we need.
+    tlen = (src.size / 3) * 4;
+    rem = src.size % 3;
+    if(rem == 1) {
+        tlen += 2;
+    } else if(rem == 2) {
+        tlen += 3;
+    }
+
+    if(!enif_alloc_binary(tlen, st->tgt)) {
+        ret = make_error(env, priv, priv->atom_nomem);
+        goto error;
+    }
+
+    status = cseq_b64url_encode(env, &src, st);
+
+    if(status == ST_OK) {
+        ret = enif_make_binary(env, st->tgt);
+        enif_free(st->tgt);
+        st->tgt = NULL;
+        enif_release_resource(st);
+        return make_ok(env, priv, ret);
+    } else {
+        assert(status == ST_PARTIAL && "invalid status");
+        ret = enif_make_resource(env, st);
+        enif_release_resource(st);
+        return make_partial(env, priv, ret);
+    }
+
+error:
+    if(st != NULL) {
+        enif_release_resource(st);
+    }
+
+    return ret;
+}
+
+
+static ENTERM
+cseq_b64url_encode_cont(ErlNifEnv* env, int argc, const ENTERM argv[])
+{
+    ErlNifBinary src;
+    cseq_priv* priv = (cseq_priv*) enif_priv_data(env);
+    cseq_st* st = NULL;
+    ENTERM ret;
+    int status;
+
+    if(argc != 2) {
+        return enif_make_badarg(env);
+    }
+
+    if(!enif_inspect_iolist_as_binary(env, argv[0], &src)) {
+        return enif_make_badarg(env);
+    }
+
+    if(src.size <= 0) {
+        return enif_make_badarg(env);
+    }
+
+    if(!enif_get_resource(env, argv[1], priv->res_st, (void**) &st)) {
+        return enif_make_badarg(env);
+    }
+
+    if(!check_pid(env, st)) {
+        return enif_make_badarg(env);
+    }
+
+    if(src.size != st->len) {
+        return enif_make_badarg(env);
+    }
+
+    status = cseq_b64url_encode(env, &src, st);
+
+    if(status == ST_OK) {
+        ret = enif_make_binary(env, st->tgt);
+        st->tgt = NULL;
+        enif_release_resource(st);
+        return make_ok(env, priv, ret);
+    } else {
+        assert(status == ST_PARTIAL && "invalid status");
+        ret = enif_make_resource(env, st);
+        enif_release_resource(st);
+        return make_partial(env, priv, ret);
+    }
+
+    assert(0 == 1 && "Invalid status from cseq_b64url_encode");
+}
+
+
+static ENTERM
+cseq_b64url_decode_init(ErlNifEnv* env, int argc, const ENTERM argv[])
+{
+    return enif_make_badarg(env);
+}
+
+
+static ENTERM
+cseq_b64url_decode_cont(ErlNifEnv* env, int argc, const ENTERM argv[])
+{
+    return enif_make_badarg(env);
+}
+
+
+static ErlNifFunc funcs[] = {
+    {"encode_init", 1, cseq_b64url_encode_init},
+    {"encode_cont", 2, cseq_b64url_encode_cont},
+    {"decode_init", 1, cseq_b64url_decode_init},
+    {"decode_cont", 2, cseq_b64url_decode_cont}
+};
+
+
+ERL_NIF_INIT(couch_seqs_b64url, funcs, &load, &reload, &upgrade, &unload);
+
+

http://git-wip-us.apache.org/repos/asf/couchdb-b64url/blob/13e21f49/rebar.config
----------------------------------------------------------------------
diff --git a/rebar.config b/rebar.config
new file mode 100644
index 0000000..6f2ca46
--- /dev/null
+++ b/rebar.config
@@ -0,0 +1,12 @@
+{port_specs, [
+    {"priv/couch_seqs_b64.so", ["c_src/*.c"]}
+]}.
+
+{port_env, [
+    % Development compilation
+    % {".*", "CFLAGS", "$CFLAGS -g -Wall -Werror -fPIC"}
+
+    % Production compilation
+    {".*", "CFLAGS", "$CFLAGS -Wall -Werror -DNDEBUG -O3"}
+]}.
+

http://git-wip-us.apache.org/repos/asf/couchdb-b64url/blob/13e21f49/src/couch_seqs.app.src
----------------------------------------------------------------------
diff --git a/src/couch_seqs.app.src b/src/couch_seqs.app.src
new file mode 100644
index 0000000..44b6a3d
--- /dev/null
+++ b/src/couch_seqs.app.src
@@ -0,0 +1,6 @@
+{application, couch_seqs, [
+    {description, "A library for handling couch sequences"},
+    {vsn, git},
+    {registered, []},
+    {applications, [kernel]}
+]}.

http://git-wip-us.apache.org/repos/asf/couchdb-b64url/blob/13e21f49/src/couch_seqs_b64url.erl
----------------------------------------------------------------------
diff --git a/src/couch_seqs_b64url.erl b/src/couch_seqs_b64url.erl
new file mode 100644
index 0000000..bc1830b
--- /dev/null
+++ b/src/couch_seqs_b64url.erl
@@ -0,0 +1,72 @@
+-module(couch_seqs_b64url).
+-on_load(init/0).
+
+
+-export([
+    encode/1,
+    decode/1
+]).
+
+
+-define(NOT_LOADED, not_loaded(?LINE)).
+
+
+-spec encode(iodata()) -> binary().
+encode(IoData) ->
+    case encode_init(IoData) of
+        {ok, Bin} ->
+            Bin;
+        {partial, St} ->
+            encode_loop(IoData, St)
+    end.
+
+
+-spec decode(iodata()) -> binary() | {error, any()}.
+decode(IoData) ->
+    case decode_init(IoData) of
+        {ok, Bin} ->
+            Bin;
+        {partial, St} ->
+            decode_loop(IoData, St)
+    end.
+
+
+init() ->
+    PrivDir = case code:priv_dir(?MODULE) of
+        {error, _} ->
+            EbinDir = filename:dirname(code:which(?MODULE)),
+            AppPath = filename:dirname(EbinDir),
+            filename:join(AppPath, "priv");
+        Path ->
+            Path
+    end,
+    erlang:load_nif(filename:join(PrivDir, "couch_seqs_b64"), 0).
+
+
+encode_loop(IoData, St) ->
+    case encode_cont(IoData, St) of
+        {ok, Bin} ->
+            Bin;
+        {partial, St} ->
+            encode_loop(IoData, St)
+    end.
+
+
+decode_loop(IoData, St) ->
+    case decode_cont(IoData, St) of
+        {ok, Bin} ->
+            Bin;
+        {partial, St} ->
+            decode_loop(IoData, St)
+    end.
+
+
+encode_init(_) -> ?NOT_LOADED.
+encode_cont(_, _) -> ?NOT_LOADED.
+decode_init(_) -> ?NOT_LOADED.
+decode_cont(_, _) -> ?NOT_LOADED.
+
+
+not_loaded(Line) ->
+    erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}).
+

http://git-wip-us.apache.org/repos/asf/couchdb-b64url/blob/13e21f49/test/couch_seqs_b64url_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_seqs_b64url_tests.erl b/test/couch_seqs_b64url_tests.erl
new file mode 100644
index 0000000..0e1089a
--- /dev/null
+++ b/test/couch_seqs_b64url_tests.erl
@@ -0,0 +1,35 @@
+-module(couch_seqs_b64url_tests).
+-compile(export_all).
+
+-include_lib("proper/include/proper.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+proper_test_() ->
+    PropErOpts = [
+        {to_file, user},
+        {max_size, 524288},
+        {numtests, 1000}
+    ],
+    {timeout, 3600, ?_assertEqual([], proper:module(?MODULE, PropErOpts))}.
+
+
+prop_encode_binary() ->
+    ?FORALL(Bin, binary(),
+        couch_seqs_b64url:encode(Bin) == couch_encode_base64url(Bin)
+    ).
+
+
+couch_encode_base64url(Data) ->
+    Url1 = iolist_to_binary(re:replace(base64:encode(Url), "=+$", "")),
+    Url2 = iolist_to_binary(re:replace(Url1, "/", "_", [global])),
+    iolist_to_binary(re:replace(Url2, "\\+", "-", [global])).
+
+
+couch_decode_base64url(Data) ->
+    Url1 = re:replace(iolist_to_binary(Url64), "-", "+", [global]),
+    Url2 = iolist_to_binary(
+        re:replace(iolist_to_binary(Url1), "_", "/", [global])
+    ),
+    Padding = list_to_binary(lists:duplicate((4 - size(Url2) rem 4) rem 4, $=)),
+    base64:decode(<<Url2/binary, Padding/binary>>).


Mime
View raw message