diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012, Magnus Therning
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    - Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    - Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    - Neither the name of Magnus Therning nor the names of other contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,22 @@
+#! /usr/bin/env runhaskell
+
+{- Copyright © 2007 Magnus Therning
+ -
+ - This file is part of dataenc.
+ -
+ - Dataenc is free software: you can redistribute it and/or modify it under
+ - the terms of the GNU Lesser General Public License as published by the
+ - Free Software Foundation, either version 3 of the License, or (at your
+ - option) any later version.
+ -
+ - Dataenc is distributed in the hope that it will be useful, but WITHOUT
+ - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ - FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ - License for more details.
+ -
+ - You should have received a copy of the GNU Lesser General Public License
+ - along with dataenc.  If not, see <http://www.gnu.org/licenses/>
+ -}
+
+import Distribution.Simple
+main = defaultMain
diff --git a/csrc/b16.c b/csrc/b16.c
new file mode 100644
--- /dev/null
+++ b/csrc/b16.c
@@ -0,0 +1,75 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#include <assert.h>
+
+#include "b16.h"
+
+static char const encmap[] = "0123456789ABCDEF";
+
+void b16_enc(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    size_t od = *dstlen, i;
+
+    assert(src || srclen == 0);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    for(i = 0, *dstlen = 0; i < srclen && *dstlen + 1 < od; i++, *dstlen += 2) {
+        uint8_t o0 = src[i] >> 4, o1 = src[i] & 0x0f;
+        dst[*dstlen] = encmap[o0];
+        dst[*dstlen + 1] = encmap[o1];
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+}
+
+static uint8_t const decmap[] = {
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+};
+
+int b16_dec(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    size_t od = *dstlen, i;
+    int res = 0;
+
+    assert(src || srclen == 0);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    for(i = 0, *dstlen = 0; i < srclen && *dstlen < od; i += 2, (*dstlen)++) {
+        if(i + 1 >= srclen) { res = 1; break; }
+        uint8_t o0 = decmap[src[i]], o1 = decmap[src[i + 1]];
+        if((o0 | o1) & 0xf0) { res = 1; break; }
+        else dst[*dstlen] = o0 << 4 | o1;
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+
+    return(res);
+}
diff --git a/csrc/b16.h b/csrc/b16.h
new file mode 100644
--- /dev/null
+++ b/csrc/b16.h
@@ -0,0 +1,17 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#ifndef _B16_H_
+#define _B16_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+void b16_enc(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b16_dec(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+
+#endif
diff --git a/csrc/b32.c b/csrc/b32.c
new file mode 100644
--- /dev/null
+++ b/csrc/b32.c
@@ -0,0 +1,245 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#include <assert.h>
+
+#include "b32.h"
+
+static char const encmap[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+void b32_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    size_t od = *dstlen, i;
+
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    for(i = 0, *dstlen = 0; i + 5 <= srclen && *dstlen + 8 <= od; i += 5, *dstlen += 8) {
+        int32_t o0, o1, o2, o3, o4, o5, o6, o7;
+        o0 = src[i] >> 3;
+        o1 = ((src[i] << 2) | (src[i+1] >> 6)) & 0x1f;
+        o2 = (src[i+1] >> 1) & 0x1f;
+        o3 = ((src[i+1] << 4) | (src[i+2] >> 4)) & 0x1f;
+        o4 = ((src[i+2] << 1) | (src[i+3] >> 7)) & 0x1f;
+        o5 = (src[i+3] >>2) & 0x1f;
+        o6 = ((src[i+3] << 3) | (src[i+4] >> 5)) & 0x1f;
+        o7 = src[i+4] & 0x1f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+        *dst++ = encmap[o4];
+        *dst++ = encmap[o5];
+        *dst++ = encmap[o6];
+        *dst++ = encmap[o7];
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+}
+
+int b32_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    switch(srclen) {
+        int32_t o0, o1, o2, o3, o4, o5, o6, o7;
+    case 0:
+        *dstlen = 0;
+        return(0);
+        break;
+    case 1:
+        o0 = src[0] >> 3;
+        o1 = (src[0] << 2) & 0x1f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dstlen = 8;
+        return(0);
+        break;
+    case 2:
+        o0 = src[0] >> 3;
+        o1 = ((src[0] << 2) | (src[1] >> 6)) & 0x1f;
+        o2 = (src[1] >> 1) & 0x1f;
+        o3 = src[1] << 4 & 0x1f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dstlen = 8;
+        return(0);
+        break;
+    case 3:
+        o0 = src[0] >> 3;
+        o1 = ((src[0] << 2) | (src[1] >> 6)) & 0x1f;
+        o2 = (src[1] >> 1) & 0x1f;
+        o3 = ((src[1] << 4) | (src[2] >> 4)) & 0x1f;
+        o4 = (src[2] << 1) & 0x1f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+        *dst++ = encmap[o4];
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dstlen = 8;
+        return(0);
+        break;
+    case 4:
+        o0 = src[0] >> 3;
+        o1 = ((src[0] << 2) | (src[1] >> 6)) & 0x1f;
+        o2 = (src[1] >> 1) & 0x1f;
+        o3 = ((src[1] << 4) | (src[2] >> 4)) & 0x1f;
+        o4 = ((src[2] << 1) | (src[3] >> 7)) & 0x1f;
+        o5 = (src[3] >>2) & 0x1f;
+        o6 = (src[3] << 3) & 0x1f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+        *dst++ = encmap[o4];
+        *dst++ = encmap[o5];
+        *dst++ = encmap[o6];
+        *dst++ = '=';
+        *dstlen = 8;
+        return(0);
+        break;
+    default:
+        return(1);
+        break;
+    }
+}
+
+// decode map, 0x80 = not allowed, 0x40 = end char
+static uint8_t const decmap[] = {
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x80, 0x80,
+    0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
+
+int b32_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    size_t od = *dstlen, i;
+    int res = 0;
+
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    for(i = 0, *dstlen = 0; i + 8 <= srclen && *dstlen + 5 <= od; i += 8, *dstlen += 5) {
+        uint8_t o0, o1, o2, o3, o4, o5, o6, o7;
+
+        o0 = decmap[src[i]];
+        o1 = decmap[src[i+1]];
+        o2 = decmap[src[i+2]];
+        o3 = decmap[src[i+3]];
+        o4 = decmap[src[i+4]];
+        o5 = decmap[src[i+5]];
+        o6 = decmap[src[i+6]];
+        o7 = decmap[src[i+7]];
+        if(!(0xc0 & (o0 | o1 | o2 | o3 | o4 | o5 | o6 | o7))) { // no illegal chars, and no '='
+            *dst++ = (o0 << 3) | (o1 >> 2);
+            *dst++ = (o1 << 6) | (o2 << 1) | (o3 >> 4);
+            *dst++ = (o3 << 4) | (o4 >> 1);
+            *dst++ = (o4 << 7) | (o5 << 2) | (o6 >> 3);
+            *dst++ = (o6 << 5) | o7;
+        } else if(!(0xc0 & (o0 | o1)) && (0x40 & o2 & o3 & o4 & o5 & o6 & o7) // two legal chars, six '='
+            || !(0xc0 & (o0 | o1 | o2 | o3)) && (0x40 & o4 & o5 & o6 & o7) // four legal chars, four '='
+            || !(0xc0 & (o0 | o1 | o2 | o3 | o4)) && (0x40 & o5 & o6 & o7) // five legal chars, three '='
+            || !(0xc0 & (o0 | o1 | o2 | o3 | o4 | o5 | o6)) && (0x40 & o7)) { // seven legal chars, one '='
+            res = 0;
+            break;
+        } else {
+            res = 1;
+            break;
+        }
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+    return(res);
+}
+
+int b32_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    uint8_t o0, o1, o2, o3, o4, o5, o6, o7;
+
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    if(0 == srclen) {
+        *dstlen = 0;
+        return(0);
+    }
+    o0 = decmap[src[0]];
+    o1 = decmap[src[1]];
+    o2 = decmap[src[2]];
+    o3 = decmap[src[3]];
+    o4 = decmap[src[4]];
+    o5 = decmap[src[5]];
+    o6 = decmap[src[6]];
+    o7 = decmap[src[7]];
+    if(!(0xc0 & (o0 | o1)) && (0x40 & o2 & o3 & o4 & o5 & o6 & o7)) { // two legal chars, six '='
+        *dst++ = (o0 << 3) | (o1 >> 2);
+        *dst++ = (o1 << 6);
+        *dstlen = 1;
+    } else if(!(0xc0 & (o0 | o1 | o2 | o3)) && (0x40 & o4 & o5 & o6 & o7)) { // four legal chars, four '='
+        *dst++ = (o0 << 3) | (o1 >> 2);
+        *dst++ = (o1 << 6) | (o2 << 1) | (o3 >> 4);
+        *dst++ = (o3 << 4);
+        *dstlen = 2;
+    }
+    else if(!(0xc0 & (o0 | o1 | o2 | o3 | o4)) && (0x40 & o5 & o6 & o7)) { // five legal chars, three '='
+        *dst++ = (o0 << 3) | (o1 >> 2);
+        *dst++ = (o1 << 6) | (o2 << 1) | (o3 >> 4);
+        *dst++ = (o3 << 4) | (o4 >> 1);
+        *dst++ = (o4 << 7) | (o5 << 2) | (o6 >> 3);
+        *dstlen = 3;
+    } else if(!(0xc0 & (o0 | o1 | o2 | o3 | o4 | o5 | o6)) && (0x40 & o7)) { // seven legal chars, one '='
+        *dst++ = (o0 << 3) | (o1 >> 2);
+        *dst++ = (o1 << 6) | (o2 << 1) | (o3 >> 4);
+        *dst++ = (o3 << 4) | (o4 >> 1);
+        *dst++ = (o4 << 7) | (o5 << 2) | (o6 >> 3);
+        *dst++ = (o6 << 5);
+        *dstlen = 4;
+    } else
+        return(1);
+
+    return(0);
+}
diff --git a/csrc/b32.h b/csrc/b32.h
new file mode 100644
--- /dev/null
+++ b/csrc/b32.h
@@ -0,0 +1,21 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#ifndef _B32_H_
+#define _B32_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+void b32_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b32_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+int b32_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b32_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+
+#endif
diff --git a/csrc/b32hex.c b/csrc/b32hex.c
new file mode 100644
--- /dev/null
+++ b/csrc/b32hex.c
@@ -0,0 +1,245 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#include <assert.h>
+
+#include "b32hex.h"
+
+static char const encmap[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
+
+void b32h_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    size_t od = *dstlen, i;
+
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    for(i = 0, *dstlen = 0; i + 5 <= srclen && *dstlen + 8 <= od; i += 5, *dstlen += 8) {
+        int32_t o0, o1, o2, o3, o4, o5, o6, o7;
+        o0 = src[i] >> 3;
+        o1 = ((src[i] << 2) | (src[i+1] >> 6)) & 0x1f;
+        o2 = (src[i+1] >> 1) & 0x1f;
+        o3 = ((src[i+1] << 4) | (src[i+2] >> 4)) & 0x1f;
+        o4 = ((src[i+2] << 1) | (src[i+3] >> 7)) & 0x1f;
+        o5 = (src[i+3] >>2) & 0x1f;
+        o6 = ((src[i+3] << 3) | (src[i+4] >> 5)) & 0x1f;
+        o7 = src[i+4] & 0x1f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+        *dst++ = encmap[o4];
+        *dst++ = encmap[o5];
+        *dst++ = encmap[o6];
+        *dst++ = encmap[o7];
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+}
+
+int b32h_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    switch(srclen) {
+        int32_t o0, o1, o2, o3, o4, o5, o6, o7;
+    case 0:
+        *dstlen = 0;
+        return(0);
+        break;
+    case 1:
+        o0 = src[0] >> 3;
+        o1 = (src[0] << 2) & 0x1f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dstlen = 8;
+        return(0);
+        break;
+    case 2:
+        o0 = src[0] >> 3;
+        o1 = ((src[0] << 2) | (src[1] >> 6)) & 0x1f;
+        o2 = (src[1] >> 1) & 0x1f;
+        o3 = src[1] << 4 & 0x1f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dstlen = 8;
+        return(0);
+        break;
+    case 3:
+        o0 = src[0] >> 3;
+        o1 = ((src[0] << 2) | (src[1] >> 6)) & 0x1f;
+        o2 = (src[1] >> 1) & 0x1f;
+        o3 = ((src[1] << 4) | (src[2] >> 4)) & 0x1f;
+        o4 = (src[2] << 1) & 0x1f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+        *dst++ = encmap[o4];
+        *dst++ = '=';
+        *dst++ = '=';
+        *dst++ = '=';
+        *dstlen = 8;
+        return(0);
+        break;
+    case 4:
+        o0 = src[0] >> 3;
+        o1 = ((src[0] << 2) | (src[1] >> 6)) & 0x1f;
+        o2 = (src[1] >> 1) & 0x1f;
+        o3 = ((src[1] << 4) | (src[2] >> 4)) & 0x1f;
+        o4 = ((src[2] << 1) | (src[3] >> 7)) & 0x1f;
+        o5 = (src[3] >>2) & 0x1f;
+        o6 = (src[3] << 3) & 0x1f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+        *dst++ = encmap[o4];
+        *dst++ = encmap[o5];
+        *dst++ = encmap[o6];
+        *dst++ = '=';
+        *dstlen = 8;
+        return(0);
+        break;
+    default:
+        return(1);
+        break;
+    }
+}
+
+// decode map, 0x80 = not allowed, 0x40 = end char
+static uint8_t const decmap[] = {
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x80, 0x80, 0x80, 0x40, 0x80, 0x80,
+    0x80, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+    0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
+
+int b32h_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    size_t od = *dstlen, i;
+    int res = 0;
+
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    for(i = 0, *dstlen = 0; i + 8 <= srclen && *dstlen + 5 <= od; i += 8, *dstlen += 5) {
+        uint8_t o0, o1, o2, o3, o4, o5, o6, o7;
+
+        o0 = decmap[src[i]];
+        o1 = decmap[src[i+1]];
+        o2 = decmap[src[i+2]];
+        o3 = decmap[src[i+3]];
+        o4 = decmap[src[i+4]];
+        o5 = decmap[src[i+5]];
+        o6 = decmap[src[i+6]];
+        o7 = decmap[src[i+7]];
+        if(!(0xc0 & (o0 | o1 | o2 | o3 | o4 | o5 | o6 | o7))) { // no illegal chars, and no '='
+            *dst++ = (o0 << 3) | (o1 >> 2);
+            *dst++ = (o1 << 6) | (o2 << 1) | (o3 >> 4);
+            *dst++ = (o3 << 4) | (o4 >> 1);
+            *dst++ = (o4 << 7) | (o5 << 2) | (o6 >> 3);
+            *dst++ = (o6 << 5) | o7;
+        } else if(!(0xc0 & (o0 | o1)) && (0x40 & o2 & o3 & o4 & o5 & o6 & o7) // two legal chars, six '='
+            || !(0xc0 & (o0 | o1 | o2 | o3)) && (0x40 & o4 & o5 & o6 & o7) // four legal chars, four '='
+            || !(0xc0 & (o0 | o1 | o2 | o3 | o4)) && (0x40 & o5 & o6 & o7) // five legal chars, three '='
+            || !(0xc0 & (o0 | o1 | o2 | o3 | o4 | o5 | o6)) && (0x40 & o7)) { // seven legal chars, one '='
+            res = 0;
+            break;
+        } else {
+            res = 1;
+            break;
+        }
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+    return(res);
+}
+
+int b32h_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    uint8_t o0, o1, o2, o3, o4, o5, o6, o7;
+
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    if(0 == srclen) {
+        *dstlen = 0;
+        return(0);
+    }
+    o0 = decmap[src[0]];
+    o1 = decmap[src[1]];
+    o2 = decmap[src[2]];
+    o3 = decmap[src[3]];
+    o4 = decmap[src[4]];
+    o5 = decmap[src[5]];
+    o6 = decmap[src[6]];
+    o7 = decmap[src[7]];
+    if(!(0xc0 & (o0 | o1)) && (0x40 & o2 & o3 & o4 & o5 & o6 & o7)) { // two legal chars, six '='
+        *dst++ = (o0 << 3) | (o1 >> 2);
+        *dst++ = (o1 << 6);
+        *dstlen = 1;
+    } else if(!(0xc0 & (o0 | o1 | o2 | o3)) && (0x40 & o4 & o5 & o6 & o7)) { // four legal chars, four '='
+        *dst++ = (o0 << 3) | (o1 >> 2);
+        *dst++ = (o1 << 6) | (o2 << 1) | (o3 >> 4);
+        *dst++ = (o3 << 4);
+        *dstlen = 2;
+    }
+    else if(!(0xc0 & (o0 | o1 | o2 | o3 | o4)) && (0x40 & o5 & o6 & o7)) { // five legal chars, three '='
+        *dst++ = (o0 << 3) | (o1 >> 2);
+        *dst++ = (o1 << 6) | (o2 << 1) | (o3 >> 4);
+        *dst++ = (o3 << 4) | (o4 >> 1);
+        *dst++ = (o4 << 7) | (o5 << 2) | (o6 >> 3);
+        *dstlen = 3;
+    } else if(!(0xc0 & (o0 | o1 | o2 | o3 | o4 | o5 | o6)) && (0x40 & o7)) { // seven legal chars, one '='
+        *dst++ = (o0 << 3) | (o1 >> 2);
+        *dst++ = (o1 << 6) | (o2 << 1) | (o3 >> 4);
+        *dst++ = (o3 << 4) | (o4 >> 1);
+        *dst++ = (o4 << 7) | (o5 << 2) | (o6 >> 3);
+        *dst++ = (o6 << 5);
+        *dstlen = 4;
+    } else
+        return(1);
+
+    return(0);
+}
diff --git a/csrc/b32hex.h b/csrc/b32hex.h
new file mode 100644
--- /dev/null
+++ b/csrc/b32hex.h
@@ -0,0 +1,21 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#ifndef _B32_H_
+#define _B32_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+void b32h_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b32h_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+int b32h_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b32h_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+
+#endif
diff --git a/csrc/b64.c b/csrc/b64.c
new file mode 100644
--- /dev/null
+++ b/csrc/b64.c
@@ -0,0 +1,165 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#include <assert.h>
+
+#include "b64.h"
+
+static char const encmap[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+void b64_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+
+    for(i = 0, *dstlen = 0; i + 3 <= srclen && *dstlen + 4 <= od; i += 3, *dstlen += 4) {
+        int32_t o0, o1, o2, o3;
+        o0 = src[i] >> 2;
+        o1 = ((src[i] << 4) | (src[i+1] >> 4)) & 0x3f;
+        o2 = ((src[i+1] << 2) | (src[i+2] >> 6)) & 0x3f;
+        o3 = src[i+2] & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+}
+
+int b64_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    switch(srclen) {
+        int32_t o0, o1, o2, o3;
+    case 0:
+        *dstlen = 0;
+        return(0);
+        break;
+    case 1:
+        o0 = src[0] >> 2;
+        o1 = (src[0] << 4) & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = '=';
+        *dst++ = '=';
+        *dstlen = 4;
+        return(0);
+        break;
+    case 2:
+        o0 = src[0] >> 2;
+        o1 = ((src[0] << 4) | (src[1] >> 4)) & 0x3f;
+        o2 = (src[1] << 2) & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = '=';
+        *dstlen = 4;
+        return(0);
+        break;
+    default:
+        return(1);
+        break;
+    }
+}
+
+// decode map, 0x80 = not allowed, 0x40 = end char
+static uint8_t const decmap[] = {
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f,
+    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x40, 0x80, 0x80,
+    0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
+
+
+int b64_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+    int res = 0;
+
+    for(i = 0, *dstlen = 0; i + 4 <= srclen && *dstlen + 3 <= od; i += 4, *dstlen += 3) {
+        uint8_t o0, o1, o2, o3;
+
+        o0 = decmap[src[i]];
+        o1 = decmap[src[i+1]];
+        o2 = decmap[src[i+2]];
+        o3 = decmap[src[i+3]];
+        if(!(0xc0 & (o0 | o1 | o2 | o3))) { // no illegal chars, and no '='
+            *dst++ = (o0 << 2) | (o1 >> 4);
+            *dst++ = (o1 << 4) | (o2 >> 2);
+            *dst++ = (o2 << 6) | o3;
+        } else if(!(0xc0 & (o0 | o1)) && (0x40 & o2 & o3) // two legal chars, two '='
+            || !(0xc0 & (o0 | o1 | o2)) && (0x40 & o3)) { // three legal chars, one '='
+            res = 0;
+            break;
+        } else {
+            res = 1;
+            break;
+        }
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+    return(res);
+}
+
+int b64_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    uint8_t o0, o1, o2, o3;
+
+    if(0 == srclen) {
+        *dstlen = 0;
+        return(0);
+    }
+    o0 = decmap[src[0]];
+    o1 = decmap[src[1]];
+    o2 = decmap[src[2]];
+    o3 = decmap[src[3]];
+    if(!(0xc0 & (o0 | o1)) && (0x40 & o2 & o3)) { // two legal chars, two '='
+        *dst++ = (o0 << 2) | (o1 >> 4);
+        *dstlen = 1;
+    } else if(!(0xc0 & (o0 | o1 | o2)) && (0x40 & o3)) { // three legal chars, one '='
+            *dst++ = (o0 << 2) | (o1 >> 4);
+            *dst++ = (o1 << 4) | (o2 >> 2);
+            *dstlen = 2;
+    } else
+        return(1);
+
+    return(0);
+}
diff --git a/csrc/b64.h b/csrc/b64.h
new file mode 100644
--- /dev/null
+++ b/csrc/b64.h
@@ -0,0 +1,21 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#ifndef _B64_H_
+#define _B64_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+void b64_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b64_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+int b64_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b64_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+
+#endif
diff --git a/csrc/b64url.c b/csrc/b64url.c
new file mode 100644
--- /dev/null
+++ b/csrc/b64url.c
@@ -0,0 +1,165 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#include <assert.h>
+
+#include "b64url.h"
+
+static char const encmap[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+void b64u_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+
+    for(i = 0, *dstlen = 0; i + 3 <= srclen && *dstlen + 4 <= od; i += 3, *dstlen += 4) {
+        int32_t o0, o1, o2, o3;
+        o0 = src[i] >> 2;
+        o1 = ((src[i] << 4) | (src[i+1] >> 4)) & 0x3f;
+        o2 = ((src[i+1] << 2) | (src[i+2] >> 6)) & 0x3f;
+        o3 = src[i+2] & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+}
+
+int b64u_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    switch(srclen) {
+        int32_t o0, o1, o2, o3;
+    case 0:
+        *dstlen = 0;
+        return(0);
+        break;
+    case 1:
+        o0 = src[0] >> 2;
+        o1 = (src[0] << 4) & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = '=';
+        *dst++ = '=';
+        *dstlen = 4;
+        return(0);
+        break;
+    case 2:
+        o0 = src[0] >> 2;
+        o1 = ((src[0] << 4) | (src[1] >> 4)) & 0x3f;
+        o2 = (src[1] << 2) & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = '=';
+        *dstlen = 4;
+        return(0);
+        break;
+    default:
+        return(1);
+        break;
+    }
+}
+
+// decode map, 0x80 = not allowed, 0x40 = end char
+static uint8_t const decmap[] = {
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80,
+    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x40, 0x80, 0x80,
+    0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x3f,
+    0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
+
+
+int b64u_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+    int res = 0;
+
+    for(i = 0, *dstlen = 0; i + 4 <= srclen && *dstlen + 3 <= od; i += 4, *dstlen += 3) {
+        uint8_t o0, o1, o2, o3;
+
+        o0 = decmap[src[i]];
+        o1 = decmap[src[i+1]];
+        o2 = decmap[src[i+2]];
+        o3 = decmap[src[i+3]];
+        if(!(0xc0 & (o0 | o1 | o2 | o3))) { // no illegal chars, and no '='
+            *dst++ = (o0 << 2) | (o1 >> 4);
+            *dst++ = (o1 << 4) | (o2 >> 2);
+            *dst++ = (o2 << 6) | o3;
+        } else if(!(0xc0 & (o0 | o1)) && (0x40 & o2 & o3) // two legal chars, two '='
+            || !(0xc0 & (o0 | o1 | o2)) && (0x40 & o3)) { // three legal chars, one '='
+            res = 0;
+            break;
+        } else {
+            res = 1;
+            break;
+        }
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+    return(res);
+}
+
+int b64u_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    uint8_t o0, o1, o2, o3;
+
+    if(0 == srclen) {
+        *dstlen = 0;
+        return(0);
+    }
+    o0 = decmap[src[0]];
+    o1 = decmap[src[1]];
+    o2 = decmap[src[2]];
+    o3 = decmap[src[3]];
+    if(!(0xc0 & (o0 | o1)) && (0x40 & o2 & o3)) { // two legal chars, two '='
+        *dst++ = (o0 << 2) | (o1 >> 4);
+        *dstlen = 1;
+    } else if(!(0xc0 & (o0 | o1 | o2)) && (0x40 & o3)) { // three legal chars, one '='
+            *dst++ = (o0 << 2) | (o1 >> 4);
+            *dst++ = (o1 << 4) | (o2 >> 2);
+            *dstlen = 2;
+    } else
+        return(1);
+
+    return(0);
+}
diff --git a/csrc/b64url.h b/csrc/b64url.h
new file mode 100644
--- /dev/null
+++ b/csrc/b64url.h
@@ -0,0 +1,21 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#ifndef _B64URL_H_
+#define _B64URL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+void b64u_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b64u_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+int b64u_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b64u_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+
+#endif
diff --git a/csrc/b85.c b/csrc/b85.c
new file mode 100644
--- /dev/null
+++ b/csrc/b85.c
@@ -0,0 +1,236 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#include <assert.h>
+#include <string.h>
+
+#include "b85.h"
+
+uint8_t zeroes[] = { 0, 0, 0, 0 };
+uint8_t spaces[] = { 0x20, 0x20, 0x20, 0x20 };
+
+void b85_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src ||  0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+
+    for(i = 0, *dstlen = 0; i + 4 <= srclen && *dstlen < od; i += 4) {
+        if(memcmp(src + i, zeroes, 4) == 0) {
+            dst[*dstlen] = 'z';
+            *dstlen += 1;
+        } else if(memcmp(src + i, spaces, 4) == 0) {
+            dst[*dstlen] = 'y';
+            *dstlen += 1;
+        } else {
+            if(od < *dstlen + 5) goto exit;
+            uint32_t v = (src[i] << 24) | (src[i+1] << 16) | (src[i+2] << 8) | src[i+3];
+            dst[*dstlen + 4] = v % 85 + 33; v /= 85;
+            dst[*dstlen + 3] = v % 85 + 33; v /= 85;
+            dst[*dstlen + 2] = v % 85 + 33; v /= 85;
+            dst[*dstlen + 1] = v % 85 + 33; v /= 85;
+            dst[*dstlen] = v % 85 + 33;
+            *dstlen += 5;
+        }
+    }
+exit:
+    *rem = src + i;
+    *remlen = srclen - i;
+}
+
+int b85_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src ||  0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    switch(srclen) {
+        uint32_t v;
+
+    case 0:
+        *dstlen = 0;
+        return(0);
+        break;
+    case 1:
+        v = (src[0] << 24) | 1;
+        v /= 85; v /= 85; v /= 85;
+        dst[1] = v % 85 + 33; v /= 85;
+        dst[0] = v % 85 + 33;
+        *dstlen = 2;
+        return(0);
+        break;
+    case 2:
+        v = (src[0] << 24) | (src[1] << 16) | 1;
+        v /= 85; v /= 85;
+        dst[2] = v % 85 + 33; v /= 85;
+        dst[1] = v % 85 + 33; v /= 85;
+        dst[0] = v % 85 + 33;
+        *dstlen = 3;
+        return(0);
+        break;
+    case 3:
+        v = (src[0] << 24) | (src[1] << 16) |(src[2] << 8) | 1;
+        v /= 85;
+        dst[3] = v % 85 + 33; v /= 85;
+        dst[2] = v % 85 + 33; v /= 85;
+        dst[1] = v % 85 + 33; v /= 85;
+        dst[0] = v % 85 + 33;
+        *dstlen = 4;
+        return(0);
+        break;
+    default:
+        return(1);
+        break;
+    }
+}
+
+static uint8_t const decmap[] = {
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+    0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
+    0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+    0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
+    0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+};
+
+int b85_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src ||  0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+    int res = 0;
+
+    for(i = 0, *dstlen = 0; i < srclen && *dstlen + 4 <= od; *dstlen += 4) {
+        switch(src[i]) {
+            uint32_t o0, o1, o2, o3, o4, v;
+        case 'z':
+            dst[*dstlen + 3] = dst[*dstlen + 2] = dst[*dstlen + 1] = dst[*dstlen] = 0;
+            i++;
+            break;
+        case 'y':
+            dst[*dstlen + 3] = dst[*dstlen + 2] = dst[*dstlen + 1] = dst[*dstlen] = 0x20;
+            i++;
+            break;
+        default:
+            if(srclen < i + 5) { res = 0; goto exit; }
+            o0 = decmap[src[i]];
+            o1 = decmap[src[i + 1]];
+            o2 = decmap[src[i + 2]];
+            o3 = decmap[src[i + 3]];
+            o4 = decmap[src[i + 4]];
+            if(0x80 & (o0 | o1 | o2 | o3 | o4)) { res = 1; goto exit; }
+            v = o0 * 52200625; // 85 ** 4
+            v += o1 * 614125; // 85 ** 3
+            v += o2 * 7225; // 85 ** 2
+            v += o3 * 85; // 85 ** 1
+            v += o4; // 85 ** 0
+            dst[*dstlen + 3] = v & 0xff; v = v >> 8;
+            dst[*dstlen + 2] = v & 0xff; v = v >> 8;
+            dst[*dstlen + 1] = v & 0xff; v = v >> 8;
+            dst[*dstlen + 0] = v & 0xff;
+            i += 5;
+            break;
+        }
+    }
+exit:
+    *rem = src + i;
+    *remlen = srclen - i;
+    return(res);
+}
+
+int b85_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src ||  0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    switch(srclen) {
+        uint32_t o0, o1, o2, o3, o4, v;
+
+    case 0:
+        *dstlen = 0;
+        return(0);
+        break;
+    case 2:
+        o0 = decmap[src[0]];
+        o1 = decmap[src[1]];
+        o2 = decmap['u'];
+        o3 = decmap['u'];
+        o4 = decmap['u'];
+        if(0x80 & ( o0 | o1)) { return(1); }
+        v = o0 * 52200625; // 85 ** 4
+        v += o1 * 614125; // 85 ** 3
+        v += o2 * 7225; // 85 ** 2
+        v += o3 * 85; // 85 ** 1
+        v += o4; // 85 ** 0
+        v = v >> 24;
+        dst[0] = v & 0xff;
+        *dstlen = 1;
+        return(0);
+        break;
+    case 3:
+        o0 = decmap[src[0]];
+        o1 = decmap[src[1]];
+        o2 = decmap[src[2]];
+        o3 = decmap['u'];
+        o4 = decmap['u'];
+        if(0x80 & ( o0 | o1 | o2)) { return(1); }
+        v = o0 * 52200625; // 85 ** 4
+        v += o1 * 614125; // 85 ** 3
+        v += o2 * 7225; // 85 ** 2
+        v += o3 * 85; // 85 ** 1
+        v += o4; // 85 ** 0
+        v = v >> 16;
+        dst[1] = v & 0xff; v = v >> 8;
+        dst[0] = v & 0xff;
+        *dstlen = 2;
+        return(0);
+        break;
+    case 4:
+        o0 = decmap[src[0]];
+        o1 = decmap[src[1]];
+        o2 = decmap[src[2]];
+        o3 = decmap[src[3]];
+        o4 = decmap['u'];
+        if(0x80 & ( o0 | o1 | o2 | o3)) { return(1); }
+        v = o0 * 52200625; // 85 ** 4
+        v += o1 * 614125; // 85 ** 3
+        v += o2 * 7225; // 85 ** 2
+        v += o3 * 85; // 85 ** 1
+        v += o4; // 85 ** 0
+        v = v >> 8;
+        dst[2] = v & 0xff; v = v >> 8;
+        dst[1] = v & 0xff; v = v >> 8;
+        dst[0] = v & 0xff;
+        *dstlen = 3;
+        return(0);
+        break;
+    default:
+        return(1);
+        break;
+    }
+}
diff --git a/csrc/b85.h b/csrc/b85.h
new file mode 100644
--- /dev/null
+++ b/csrc/b85.h
@@ -0,0 +1,21 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#ifndef _B85_H_
+#define _B85_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+void b85_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b85_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+int b85_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int b85_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+
+#endif
diff --git a/csrc/qp.c b/csrc/qp.c
new file mode 100644
--- /dev/null
+++ b/csrc/qp.c
@@ -0,0 +1,91 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#include <assert.h>
+
+#include "qp.h"
+
+static char const encmap[] = "0123456789ABCDEF";
+
+void qp_enc(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || srclen == 0);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+
+    for(i = 0, *dstlen = 0; i < srclen && *dstlen < od; i++, (*dstlen)++) {
+        if((33 <= src[i] && src[i] <= 60) ||
+            (62 <= src[i] && src[i] <= 126)) {
+            dst[*dstlen] = src[i];
+        } else {
+            uint8_t o0 = src[i] >> 4, o1 = src[i] & 0x0f;
+            if(*dstlen + 3 >= od) goto exit;
+            dst[*dstlen] = '=';
+            dst[*dstlen + 1] = encmap[o0];
+            dst[*dstlen + 2] = encmap[o1];
+            *dstlen += 2;
+        }
+    }
+
+exit:
+    *rem = src + i;
+    *remlen = srclen -i;
+}
+
+static uint8_t const decmap[] = {
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+};
+
+int qp_dec(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || srclen == 0);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+    int res = 0;
+
+    for(i = 0, *dstlen = 0; i < srclen && *dstlen < od; i++, (*dstlen)++) {
+        if((33 <= src[i] && src[i] <= 60) ||
+            (62 <= src[i] && src[i] <= 126)) {
+            dst[*dstlen] = src[i];
+        } else if('=' == src[i]) {
+            if(i + 2 >= srclen) { res = 1; goto exit; }
+            uint8_t o0 = decmap[src[i + 1]], o1 = decmap[src[i + 2]];
+            if((o0 | o1) & 0xf0) { res = 1; break; }
+            dst[*dstlen] = o0 << 4 | o1;
+            i += 2;
+        } else { res = 1; goto exit; }
+    }
+
+exit:
+    *rem = src + i;
+    *remlen = srclen -i;
+
+    return(res);
+}
diff --git a/csrc/qp.h b/csrc/qp.h
new file mode 100644
--- /dev/null
+++ b/csrc/qp.h
@@ -0,0 +1,17 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#ifndef _QP_H_
+#define _QP_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+void qp_enc(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int qp_dec(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+
+#endif
diff --git a/csrc/uu.c b/csrc/uu.c
new file mode 100644
--- /dev/null
+++ b/csrc/uu.c
@@ -0,0 +1,166 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#include <assert.h>
+
+#include "uu.h"
+
+static char const encmap[] = "`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
+
+void uu_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+
+    for(i = 0, *dstlen = 0; i + 3 <= srclen && *dstlen + 4 <= od; i += 3, *dstlen += 4) {
+        int32_t o0, o1, o2, o3;
+        o0 = src[i] >> 2;
+        o1 = ((src[i] << 4) | (src[i+1] >> 4)) & 0x3f;
+        o2 = ((src[i+1] << 2) | (src[i+2] >> 6)) & 0x3f;
+        o3 = src[i+2] & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+}
+
+int uu_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    switch(srclen) {
+        int32_t o0, o1, o2, o3;
+    case 0:
+        *dstlen = 0;
+        return(0);
+        break;
+    case 1:
+        o0 = src[0] >> 2;
+        o1 = (src[0] << 4) & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dstlen = 2;
+        return(0);
+        break;
+    case 2:
+        o0 = src[0] >> 2;
+        o1 = ((src[0] << 4) | (src[1] >> 4)) & 0x3f;
+        o2 = (src[1] << 2) & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dstlen = 3;
+        return(0);
+        break;
+    default:
+        return(1);
+        break;
+    }
+}
+
+// decode map, 0x80 = not allowed, 0x40 = end char
+static uint8_t const decmap[] = {
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x40, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 
+    0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
+
+int uu_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+    int res = 0;
+
+    for(i = 0, *dstlen = 0; i + 4 <= srclen && *dstlen + 3 <= od; i += 4, *dstlen += 3) {
+        uint8_t o0, o1, o2, o3;
+
+        o0 = decmap[src[i]];
+        o1 = decmap[src[i+1]];
+        o2 = decmap[src[i+2]];
+        o3 = decmap[src[i+3]];
+        if(!(0xc0 & (o0 | o1 | o2 | o3))) { // no illegal chars, and no ' '
+            *dst++ = (o0 << 2) | (o1 >> 4);
+            *dst++ = (o1 << 4) | (o2 >> 2);
+            *dst++ = (o2 << 6) | o3;
+        } else {
+            res = 1;
+            break;
+        }
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+    return(res);
+}
+
+int uu_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    uint8_t o0, o1, o2, o3;
+
+    switch(srclen) {
+    case 0:
+        *dstlen = 0;
+        return(0);
+        break;
+    case 2:
+        o0 = decmap[src[0]];
+        o1 = decmap[src[1]];
+        if(0xc0 & (o0 | o1)) goto error;
+        dst[0] = (o0 << 2) | (o1 >> 4);
+        *dstlen = 1;
+        return(0);
+        break;
+    case 3:
+        o0 = decmap[src[0]];
+        o1 = decmap[src[1]];
+        o2 = decmap[src[2]];
+        if(0xc0 & (o0 | o1 | o2)) goto error;
+        dst[0] = (o0 << 2) | (o1 >> 4);
+        dst[1] = (o1 << 4) | (o2 >> 2);
+        *dstlen = 2;
+        return(0);
+        break;
+    }
+
+error:
+    *dstlen = 0;
+    return(1);
+}
diff --git a/csrc/uu.h b/csrc/uu.h
new file mode 100644
--- /dev/null
+++ b/csrc/uu.h
@@ -0,0 +1,21 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#ifndef _UU_H_
+#define _UU_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+void uu_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int uu_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+int uu_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int uu_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+
+#endif
diff --git a/csrc/xx.c b/csrc/xx.c
new file mode 100644
--- /dev/null
+++ b/csrc/xx.c
@@ -0,0 +1,167 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#include <assert.h>
+
+#include "xx.h"
+
+static char const encmap[] = "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+void xx_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+
+    for(i = 0, *dstlen = 0; i + 3 <= srclen && *dstlen + 4 <= od; i += 3, *dstlen += 4) {
+        int32_t o0, o1, o2, o3;
+        o0 = src[i] >> 2;
+        o1 = ((src[i] << 4) | (src[i+1] >> 4)) & 0x3f;
+        o2 = ((src[i+1] << 2) | (src[i+2] >> 6)) & 0x3f;
+        o3 = src[i+2] & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dst++ = encmap[o3];
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+}
+
+int xx_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    switch(srclen) {
+        int32_t o0, o1, o2, o3;
+    case 0:
+        *dstlen = 0;
+        return(0);
+        break;
+    case 1:
+        o0 = src[0] >> 2;
+        o1 = (src[0] << 4) & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dstlen = 2;
+        return(0);
+        break;
+    case 2:
+        o0 = src[0] >> 2;
+        o1 = ((src[0] << 4) | (src[1] >> 4)) & 0x3f;
+        o2 = (src[1] << 2) & 0x3f;
+        *dst++ = encmap[o0];
+        *dst++ = encmap[o1];
+        *dst++ = encmap[o2];
+        *dstlen = 3;
+        return(0);
+        break;
+    default:
+        return(1);
+        break;
+    }
+}
+
+// decode map, 0x80 = not allowed, 0x40 = end char
+static uint8_t const decmap[] = {
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x40, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x01, 0x80, 0x80, 
+    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 
+    0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 
+    0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+};
+
+int xx_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+    int res = 0;
+
+    for(i = 0, *dstlen = 0; i + 4 <= srclen && *dstlen + 3 <= od; i += 4, *dstlen += 3) {
+        uint8_t o0, o1, o2, o3;
+
+        o0 = decmap[src[i]];
+        o1 = decmap[src[i+1]];
+        o2 = decmap[src[i+2]];
+        o3 = decmap[src[i+3]];
+        if(!(0xc0 & (o0 | o1 | o2 | o3))) { // no illegal chars, and no ' '
+            *dst++ = (o0 << 2) | (o1 >> 4);
+            *dst++ = (o1 << 4) | (o2 >> 2);
+            *dst++ = (o2 << 6) | o3;
+        } else {
+            res = 1;
+            break;
+        }
+    }
+
+    *rem = src + i;
+    *remlen = srclen - i;
+    return(res);
+}
+
+int xx_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+
+    uint8_t o0, o1, o2, o3;
+
+    switch(srclen) {
+    case 0:
+        *dstlen = 0;
+        return(0);
+        break;
+    case 2:
+        o0 = decmap[src[0]];
+        o1 = decmap[src[1]];
+        if(0xc0 & (o0 | o1)) goto error;
+        dst[0] = (o0 << 2) | (o1 >> 4);
+        *dstlen = 1;
+        return(0);
+        break;
+    case 3:
+        o0 = decmap[src[0]];
+        o1 = decmap[src[1]];
+        o2 = decmap[src[2]];
+        if(0xc0 & (o0 | o1 | o2)) goto error;
+        dst[0] = (o0 << 2) | (o1 >> 4);
+        dst[1] = (o1 << 4) | (o2 >> 2);
+        *dstlen = 2;
+        return(0);
+        break;
+    }
+
+error:
+    *dstlen = 0;
+    return(1);
+}
diff --git a/csrc/xx.h b/csrc/xx.h
new file mode 100644
--- /dev/null
+++ b/csrc/xx.h
@@ -0,0 +1,21 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#ifndef _XX_H_
+#define _XX_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+void xx_enc_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int xx_enc_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+int xx_dec_part(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int xx_dec_final(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen);
+
+#endif
diff --git a/csrc/yenc.c b/csrc/yenc.c
new file mode 100644
--- /dev/null
+++ b/csrc/yenc.c
@@ -0,0 +1,66 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#include <assert.h>
+
+#include "yenc.h"
+
+void y_enc(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+
+    for(i = 0, *dstlen = 0; i < srclen && *dstlen < od; i++, (*dstlen)++) {
+        switch(src[i]) {
+        case 19:
+        case 214:
+        case 224:
+        case 227:
+            if(*dstlen >= od - 1) goto exit; // is there room for 2 chars in dst?
+            dst[(*dstlen)++] = 61;
+            dst[*dstlen] = src[i] + 106;
+            break;
+        default:
+            dst[*dstlen] = src[i] + 42;
+            break;
+        }
+    }
+
+exit:
+    *rem = src + i;
+    *remlen = srclen - i;
+}
+
+int y_dec(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen)
+{
+    assert(src || 0 == srclen);
+    assert(dst);
+    assert(dstlen);
+    assert(rem);
+    assert(remlen);
+
+    size_t od = *dstlen, i;
+
+    for(i = 0, *dstlen = 0; i < srclen && *dstlen < od; i++, (*dstlen)++) {
+        if(61 == src[i]) {
+            if(srclen <= i + 1) goto exit;
+            dst[*dstlen] = src[++i] - 106;
+        } else
+            dst[*dstlen] = src[i] - 42;
+    }
+
+exit:
+    *rem = src + i;
+    *remlen = srclen - i;
+
+    return(0);
+}
diff --git a/csrc/yenc.h b/csrc/yenc.h
new file mode 100644
--- /dev/null
+++ b/csrc/yenc.h
@@ -0,0 +1,17 @@
+// Copyright: (c) Magnus Therning, 2012
+// License: BSD3, found in the LICENSE file
+
+#ifndef _YENC_H_
+#define _YENC_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+void y_enc(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+int y_dec(uint8_t const *src, size_t srclen,
+    uint8_t *dst, size_t *dstlen,
+    uint8_t const **rem, size_t *remlen);
+
+#endif
diff --git a/sandi.cabal b/sandi.cabal
new file mode 100644
--- /dev/null
+++ b/sandi.cabal
@@ -0,0 +1,71 @@
+name: sandi
+version: 0.1
+license: BSD3
+license-file: LICENSE
+cabal-version: >= 1.8
+build-type: Simple
+author: Magnus Therning
+maintainer: magnus@therning.org
+homepage: http://hackage.haskell.org/package/sandi
+copyright: Magnus Therning, 2012
+category: Codec
+synopsis: Data encoding library
+description: Reasonably fast data encoding library.
+extra-source-files: csrc/*.h
+
+source-repository head
+    type: git
+    location: https://github.com/magthe/sandi
+
+flag tests
+    description: Build tests
+    default: False
+
+library
+    hs-source-dirs: src
+    c-sources:
+        csrc/b16.c
+        csrc/b32.c
+        csrc/b32hex.c
+        csrc/b64.c
+        csrc/b64url.c
+        csrc/b85.c
+        csrc/qp.c
+        csrc/uu.c
+        csrc/xx.c
+        csrc/yenc.c
+    include-dirs: csrc
+    build-depends:
+        base ==4.6.*,
+        bytestring ==0.10.*
+    exposed-modules:
+            Codec.Binary.Base16
+            Codec.Binary.Base32
+            Codec.Binary.Base32Hex
+            Codec.Binary.Base64
+            Codec.Binary.Base64Url
+            Codec.Binary.Base85
+            Codec.Binary.QuotedPrintable
+            Codec.Binary.Uu
+            Codec.Binary.Xx
+            Codec.Binary.Yenc
+    if !flag(tests)
+        cc-options: "-DNDEBUG"
+    else
+        cc-options: "-DDEBUG"
+
+executable tests
+    main-is: Main.hs
+    hs-source-dirs: test-src
+    if flag(tests)
+        buildable: True
+        build-depends:
+            sandi,
+            base ==4.6.*,
+            bytestring ==0.10.*,
+            HUnit ==1.2.*,
+            test-framework-th ==0.2.*,
+            test-framework ==0.6.*,
+            test-framework-hunit ==0.2.*
+    else
+        buildable: False
diff --git a/src/Codec/Binary/Base16.hs b/src/Codec/Binary/Base16.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Base16.hs
@@ -0,0 +1,96 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module: Codec.Binary.Base16
+-- Copyright: (c) 2012 Magnus Therning
+-- License: BSD3
+--
+-- Implemention of base 16 encoding (hex encoding) as specified in RFC 4648
+-- (<http://tools.ietf.org/html/rfc4648>).
+module Codec.Binary.Base16
+    ( b16_enc
+    , b16_dec
+    , encode
+    , decode
+    ) where
+
+import Foreign
+import Foreign.C.Types
+import System.IO.Unsafe as U
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Unsafe as BSU
+
+castEnum :: (Enum a, Enum b) => a -> b
+castEnum = toEnum . fromEnum
+
+foreign import ccall "static b16.h b16_enc"
+    c_b16_enc :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
+
+foreign import ccall "static b16.h b16_dec"
+    c_b16_dec :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt
+
+-- | Encoding function.
+--
+-- This function, unlike some other encoding functions in the library, simply
+-- cannot fail.  Double the length of the input string is allocated for the
+-- encoded data, which is guaranteed to hold the result.
+--
+-- >>> b16_enc $ Data.ByteString.pack [0x00]
+-- "00"
+--
+-- >>> b16_enc $ Data.ByteString.Char8.pack "foobar"
+-- "666F6F626172"
+b16_enc :: BS.ByteString
+    -> BS.ByteString -- ^ The encoded string
+b16_enc bs = U.unsafePerformIO $ BSU.unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen * 2
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                c_b16_enc (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                outBs <- BSU.unsafePackCStringFinalizer outBuf (castEnum outLen) (free outBuf)
+                return outBs
+
+-- | Decoding function.
+--
+-- The returned value on success is @Right \<decoded string>@, and on failure
+-- it's @Left (\<decoded part\>, \<undecodable part>)@.  Space equal to the
+-- length of the input string is allocated, which is more than enough to hold
+-- the decoded data.
+--
+-- >>> b16_dec $ Data.ByteString.Char8.pack "00"
+-- Right "\NUL"
+--
+-- >>> b16_dec $ Data.ByteString.Char8.pack "666F6F626172"
+-- Right "foobar"
+--
+-- >>> b16_dec $ Data.ByteString.Char8.pack "666F6F62617"
+-- Left ("fooba","7")
+b16_dec :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+b16_dec bs = U.unsafePerformIO $ BSU.unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes inLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum inLen)
+                r <- c_b16_dec (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- BSU.unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                if r == 0
+                    then return $ Right outBs
+                    else return $ Left (outBs, remBs)
+
+-- | A synonym for 'b16_enc'.
+encode :: BS.ByteString -> BS.ByteString
+encode = b16_enc
+
+-- | A synonum for 'b16_dec'.
+decode :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+decode = b16_dec
diff --git a/src/Codec/Binary/Base32.hs b/src/Codec/Binary/Base32.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Base32.hs
@@ -0,0 +1,192 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module: Codec.Binary.Base32
+-- Copyright: (c) 2012 Magnus Therning
+-- License: BSD3
+--
+-- Implemented as specified in RFC 4648 (<http://tools.ietf.org/html/rfc4648>).
+--
+-- Base32 encoding works by expanding blocks of 5 bytes of data into blocks of
+-- 8 bytes of data.  Finally it also includes a well defined ending of the
+-- encoded data to make sure the size of the final block of encoded data is 8
+-- bytes too.
+module Codec.Binary.Base32
+    ( b32_encode_part
+    , b32_encode_final
+    , b32_decode_part
+    , b32_decode_final
+    , encode
+    , decode
+    ) where
+
+import Foreign
+import Foreign.C.Types
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+import qualified Data.ByteString as BS
+import Data.ByteString.Unsafe
+import System.IO.Unsafe as U
+
+castEnum :: (Enum a, Enum b) => a -> b
+castEnum = toEnum . fromEnum
+
+foreign import ccall "static b32.h b32_enc_part"
+    c_b32_enc_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
+
+foreign import ccall "static b32.h b32_enc_final"
+    c_b32_enc_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+foreign import ccall "static b32.h b32_dec_part"
+    c_b32_dec_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt
+
+foreign import ccall "static b32.h b32_dec_final"
+    c_b32_dec_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+-- | Encoding function.
+--
+-- This function encodes as large a portion of the input as possible and
+-- returns the encoded part together with the remaining part.  Enough space is
+-- allocated for the encoding to make sure that the remaining part is less than
+-- 5 bytes long, which means it can be passed to 'b32_encode_final' as is.
+--
+-- >>> b32_encode_part $ Data.ByteString.Char8.pack "fooba"
+-- ("MZXW6YTB","")
+-- >>> b32_encode_part $ Data.ByteString.Char8.pack "foobar"
+-- ("MZXW6YTB","r")
+b32_encode_part :: BS.ByteString -> (BS.ByteString, BS.ByteString)
+b32_encode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 5 * 8
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                c_b32_enc_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer outBuf (castEnum outLen) (free outBuf)
+                return (outBs, remBs)
+
+-- | Encoding function for the final block.
+--
+-- The final block has to have a size less than 5.
+--
+-- >>> b32_encode_final $ Data.ByteString.Char8.pack "r"
+-- Just "OI======"
+--
+-- Trying to pass in too large a block result in failure:
+--
+-- >>> b32_encode_final $ Data.ByteString.Char8.pack "fooba"
+-- Nothing
+b32_encode_final :: BS.ByteString -> Maybe BS.ByteString
+b32_encode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 5
+    alloca $ \ pOutLen -> do
+        r <- c_b32_enc_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Decoding function.
+--
+-- Decode as large a portion of the input as possible.  Enough data is
+-- allocated for the output to ensure that the remainder is less than 8 bytes
+-- in size.  Success result in a @Right@ value:
+--
+-- >>> b32_decode_part $ Data.ByteString.Char8.pack "MZXW6YTB"
+-- Right ("fooba","")
+-- >>> b32_decode_part $ Data.ByteString.Char8.pack "MZXW6YTBOI======"
+-- Right ("fooba","OI======")
+--
+-- Failures occur on bad input and result in a @Left@ value:
+--
+-- >>> b32_decode_part $ Data.ByteString.Char8.pack "M=XW6YTB"
+-- Left ("","M=XW6YTB")
+b32_decode_part :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) (BS.ByteString, BS.ByteString)
+b32_decode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 8 * 5
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                r <- c_b32_dec_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                if r == 0
+                    then return $ Right (outBs, remBs)
+                    else return $ Left (outBs, remBs)
+
+-- | Decoding function for the final block.
+--
+-- The final block has to have a size of 0 or 8:
+--
+-- >>> b32_decode_final $ Data.ByteString.Char8.pack "MZXW6YQ="
+-- Just "foob"
+-- >>> b32_decode_final $ Data.ByteString.Char8.pack ""
+-- Just ""
+-- >>> b32_decode_final $ Data.ByteString.Char8.pack "MZXW6Y="
+-- Nothing
+--
+-- But it must be the encoding of a block that is less than 5 bytes:
+--
+-- >>> b32_decode_final $ encode $ Data.ByteString.Char8.pack "fooba"
+-- Nothing
+b32_decode_final :: BS.ByteString -> Maybe BS.ByteString
+b32_decode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 5
+    alloca $ \ pOutLen -> do
+        r <- c_b32_dec_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Convenience function that combines 'b32_encode_part' and
+-- 'b32_encode_final' to encode a complete string.
+--
+-- >>> encode $ Data.ByteString.Char8.pack "fooba"
+-- "MZXW6YTB"
+-- >>> encode $ Data.ByteString.Char8.pack "foobar"
+-- "MZXW6YTBOI======"
+encode :: BS.ByteString -> BS.ByteString
+encode bs = let
+        (first, rest) = b32_encode_part bs
+        Just final = b32_encode_final rest
+    in first `BS.append` final
+
+-- | Convenience function that combines 'b32_decode_part' and
+-- 'b32_decode_final' to decode a complete string.
+--
+-- >>> decode $ Data.ByteString.Char8.pack "MZXW6YTB"
+-- Right "fooba"
+-- >>> decode $ Data.ByteString.Char8.pack "MZXW6YTBOI======"
+-- Right "foobar"
+--
+-- Failures when decoding returns the decoded part and the remainder:
+--
+-- >>> decode $ Data.ByteString.Char8.pack "MZXW6YTBOI=0===="
+-- Left ("fooba","OI=0====")
+decode :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+decode bs = either
+    Left
+    (\ (first, rest) ->
+        maybe
+            (Left (first, rest))
+            (\ fin -> Right (first `BS.append` fin))
+            (b32_decode_final rest))
+    (b32_decode_part bs)
diff --git a/src/Codec/Binary/Base32Hex.hs b/src/Codec/Binary/Base32Hex.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Base32Hex.hs
@@ -0,0 +1,176 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module    : Codec.Binary.Base32Hex
+-- Copyright : (c) 2012 Magnus Therning
+-- License   : BSD3
+--
+-- Implemented as specified in RFC 4648 (<http://tools.ietf.org/html/rfc4648>).
+--
+-- This encoding is closely related to base 32 and so is its implementation, so
+-- please refer to "Codec.Binary.Base32" for further details.
+module Codec.Binary.Base32Hex
+   ( b32h_encode_part
+   , b32h_encode_final
+   , b32h_decode_part
+   , b32h_decode_final
+   , encode
+   , decode
+   ) where
+
+import Foreign
+import Foreign.C.Types
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+import qualified Data.ByteString as BS
+import Data.ByteString.Unsafe
+import System.IO.Unsafe as U
+
+castEnum :: (Enum a, Enum b) => a -> b
+castEnum = toEnum . fromEnum
+
+foreign import ccall "static b32.h b32h_enc_part"
+    c_b32h_enc_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
+
+foreign import ccall "static b32.h b32h_enc_final"
+    c_b32h_enc_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+foreign import ccall "static b32.h b32h_dec_part"
+    c_b32h_dec_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt
+
+foreign import ccall "static b32.h b32h_dec_final"
+    c_b32h_dec_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+-- | Encoding function.
+--
+-- See 'Codec.Binary.Base32.b32_encode_part'.
+--
+-- >>> b32h_encode_part $ Data.ByteString.Char8.pack "fooba"
+-- ("CPNMUOJ1","")
+-- >>> b32h_encode_part $ Data.ByteString.Char8.pack "foobar"
+-- ("CPNMUOJ1","r")
+b32h_encode_part :: BS.ByteString -> (BS.ByteString, BS.ByteString)
+b32h_encode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 5 * 8
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                c_b32h_enc_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer outBuf (castEnum outLen) (free outBuf)
+                return (outBs, remBs)
+
+-- | Encoding function for the final block.
+--
+-- See 'Codec.Binary.Base32.b32_encode_final'.
+--
+-- >>> b32h_encode_final $ Data.ByteString.Char8.pack "r"
+-- Just "E8======"
+-- >>> b32h_encode_final $ Data.ByteString.Char8.pack "fooba"
+-- Nothing
+b32h_encode_final :: BS.ByteString -> Maybe BS.ByteString
+b32h_encode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 5
+    alloca $ \ pOutLen -> do
+        r <- c_b32h_enc_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Decoding function.
+--
+-- See 'Codec.Binary.Base32.b32_decode_part'.
+--
+-- >>> b32h_decode_part $ Data.ByteString.Char8.pack "CPNMUOJ1"
+-- Right ("fooba","")
+-- >>> b32h_decode_part $ Data.ByteString.Char8.pack "CPNMUOJ1E8======"
+-- Right ("fooba","E8======")
+-- >>> b32h_decode_part $ Data.ByteString.Char8.pack "C=NMUOJ1"
+-- Left ("","C=NMUOJ1")
+b32h_decode_part :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) (BS.ByteString, BS.ByteString)
+b32h_decode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 8 * 5
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                r <- c_b32h_dec_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                if r == 0
+                    then return $ Right (outBs, remBs)
+                    else return $ Left (outBs, remBs)
+
+-- | Decoding function for the final block.
+--
+-- See 'Codec.Binary.Base32.b32_decode_final'.
+--
+-- >>> b32h_decode_final $ Data.ByteString.Char8.pack "CPNMUOG="
+-- Just "foob"
+-- >>> b32h_decode_final $ Data.ByteString.Char8.pack ""
+-- Just ""
+-- >>> b32h_decode_final $ Data.ByteString.Char8.pack "CPNMUO="
+-- Nothing
+-- >>> b32h_decode_final $ encode $ Data.ByteString.Char8.pack "fooba"
+-- Nothing
+b32h_decode_final :: BS.ByteString -> Maybe BS.ByteString
+b32h_decode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 5
+    alloca $ \ pOutLen -> do
+        r <- c_b32h_dec_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Convenience function that combines 'b32h_encode_part' and
+-- 'b32h_encode_final' to encode a complete string.
+--
+-- >>> encode $ Data.ByteString.Char8.pack "fooba"
+-- "CPNMUOJ1"
+-- >>> encode $ Data.ByteString.Char8.pack "foobar"
+-- "CPNMUOJ1E8======"
+encode :: BS.ByteString -> BS.ByteString
+encode bs = let
+        (first, rest) = b32h_encode_part bs
+        Just final = b32h_encode_final rest
+    in first `BS.append` final
+
+-- | Convenience function that combines 'b32h_decode_part' and
+-- 'b32h_decode_final' to decode a complete string.
+--
+-- >>> decode $ Data.ByteString.Char8.pack "CPNMUOJ1"
+-- Right "fooba"
+-- >>> decode $ Data.ByteString.Char8.pack "CPNMUOJ1E8======"
+-- Right "foobar"
+--
+-- Failures when decoding returns the decoded part and the remainder:
+--
+-- >>> decode $ Data.ByteString.Char8.pack "CPNMUOJ1=8======"
+-- Left ("fooba","=8======")
+decode :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+decode bs = either
+    Left
+    (\ (first, rest) ->
+        maybe
+            (Left (first, rest))
+            (\ fin -> Right (first `BS.append` fin))
+            (b32h_decode_final rest))
+    (b32h_decode_part bs)
diff --git a/src/Codec/Binary/Base64.hs b/src/Codec/Binary/Base64.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Base64.hs
@@ -0,0 +1,192 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module: Codec.Binary.Base64
+-- Copyright: (c) 2012 Magnus Therning
+-- License: BSD3
+--
+-- Implemented as specified in RFC 4648 (<http://tools.ietf.org/html/rfc4648>).
+--
+-- Base64 encoding works by expanding blocks of 3 bytes of data into blocks of
+-- 4 bytes of data.  Finally it also includes a well defined ending of the
+-- encoded data to make sure the size of the final block of encoded data is 4
+-- bytes too.
+module Codec.Binary.Base64
+    ( b64_encode_part
+    , b64_encode_final
+    , b64_decode_part
+    , b64_decode_final
+    , encode
+    , decode
+    ) where
+
+import Foreign
+import Foreign.C.Types
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+import qualified Data.ByteString as BS
+import Data.ByteString.Unsafe
+import System.IO.Unsafe as U
+
+castEnum :: (Enum a, Enum b) => a -> b
+castEnum = toEnum . fromEnum
+
+foreign import ccall "static b64.h b64_enc_part"
+    c_b64_enc_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
+
+foreign import ccall "static b64.h b64_enc_final"
+    c_b64_enc_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+foreign import ccall "static b64.h b64_dec_part"
+    c_b64_dec_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt
+
+foreign import ccall "static b64.h b64_dec_final"
+    c_b64_dec_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+-- | Encoding function.
+--
+-- This function encodes as large a portion of the input as possible and
+-- returns the encoded part together with the remaining part.  Enough space is
+-- allocated for the encoding to make sure that the remaining part is less than
+-- 3 bytes long, which means it can be passed to 'b64_encode_final' as is.
+--
+-- >>> b64_encode_part $ Data.ByteString.Char8.pack "foo"
+-- ("Zm9v","")
+-- >>> b64_encode_part $ Data.ByteString.Char8.pack "foob"
+-- ("Zm9v","b")
+b64_encode_part :: BS.ByteString -> (BS.ByteString, BS.ByteString)
+b64_encode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 3 * 4
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                c_b64_enc_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer outBuf (castEnum outLen) (free outBuf)
+                return (outBs, remBs)
+
+-- | Encoding function for the final block.
+--
+-- The final block has to have a size less than 3.
+--
+-- >>> b64_encode_final $ Data.ByteString.Char8.pack "r"
+-- Just "cg=="
+--
+-- Trying to pass in too large a block result in failure:
+--
+-- >>> b64_encode_final $ Data.ByteString.Char8.pack "foo"
+-- Nothing
+b64_encode_final :: BS.ByteString -> Maybe BS.ByteString
+b64_encode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 4
+    alloca $ \ pOutLen -> do
+        r <- c_b64_enc_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Decoding function.
+--
+-- Decode as large a portion of the input as possible.  Enough data is
+-- allocated for the output to ensure that the remainder is less than 4 bytes
+-- in size.  Success result in a @Right@ value:
+--
+-- >>> b64_decode_part $ Data.ByteString.Char8.pack "Zm9v"
+-- Right ("foo","")
+-- >>> b64_decode_part $ Data.ByteString.Char8.pack "Zm9vYmE="
+-- Right ("foo","YmE=")
+--
+-- Failures occur on bad input and result in a @Left@ value:
+--
+-- >>> b64_decode_part $ Data.ByteString.Char8.pack "Z=9v"
+-- Left ("","Z=9v")
+b64_decode_part :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) (BS.ByteString, BS.ByteString)
+b64_decode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 4 * 3
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                r <- c_b64_dec_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                if r == 0
+                    then return $ Right (outBs, remBs)
+                    else return $ Left (outBs, remBs)
+
+-- | Decoding function for the final block.
+--
+-- The final block has to have a size of 0 or 4:
+--
+-- >>> b64_decode_final $ Data.ByteString.Char8.pack "Zm8="
+-- Just "fo"
+-- >>> b64_decode_final $ Data.ByteString.Char8.pack ""
+-- Just ""
+-- >>> b64_decode_final $ Data.ByteString.Char8.pack "Zm="
+-- Nothing
+--
+-- But it must be the encoding of a block that is less than 3 bytes:
+--
+-- >>> b64_decode_final $ encode $ Data.ByteString.Char8.pack "foo"
+-- Nothing
+b64_decode_final :: BS.ByteString -> Maybe BS.ByteString
+b64_decode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 3
+    alloca $ \ pOutLen -> do
+        r <- c_b64_dec_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Convenience function that combines 'b64_encode_part' and
+-- 'b64_encode_final' to encode a complete string.
+--
+-- >>> encode $ Data.ByteString.Char8.pack "foo"
+-- "Zm9v"
+-- >>> encode $ Data.ByteString.Char8.pack "foobar"
+-- "Zm9vYmFy"
+encode :: BS.ByteString -> BS.ByteString
+encode bs = let
+        (first, rest) = b64_encode_part bs
+        Just final = b64_encode_final rest
+    in first `BS.append` final
+
+-- | Convenience function that combines 'b64_decode_part' and
+-- 'b64_decode_final' to decode a complete string.
+--
+-- >>> decode $ Data.ByteString.Char8.pack "Zm9v"
+-- Right "foo"
+-- >>> decode $ Data.ByteString.Char8.pack "Zm9vYmFy"
+-- Right "foobar"
+--
+-- Failures when decoding returns the decoded part and the remainder:
+--
+-- >>> decode $ Data.ByteString.Char8.pack "Zm9vYm=y"
+-- Left ("foo","Ym=y")
+decode :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+decode bs = either
+    Left
+    (\ (first, rest) ->
+        maybe
+            (Left (first, rest))
+            (\ fin -> Right (first `BS.append` fin))
+            (b64_decode_final rest))
+    (b64_decode_part bs)
diff --git a/src/Codec/Binary/Base64Url.hs b/src/Codec/Binary/Base64Url.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Base64Url.hs
@@ -0,0 +1,123 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module: Codec.Binary.Base64Url
+-- Copyright: (c) 2012 Magnus Therning
+-- License: BSD3
+--
+-- Implemented as specified in RFC 4648 (<http://tools.ietf.org/html/rfc4648>).
+--
+-- The difference compared to vanilla Base64 encoding is just in two
+-- characters.  In Base64 the characters @/+@ are used, and in Base64Url they
+-- are replaced by @_-@ respectively.
+--
+-- Please refer to "Codec.Binary.Base64" for the details of all functions in
+-- this module.
+module Codec.Binary.Base64Url
+    ( b64u_encode_part
+    , b64u_encode_final
+    , b64url_decode_part
+    , b64url_decode_final
+    , encode
+    , decode
+    ) where
+
+import Foreign
+import Foreign.C.Types
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+import qualified Data.ByteString as BS
+import Data.ByteString.Unsafe
+import System.IO.Unsafe as U
+
+castEnum :: (Enum a, Enum b) => a -> b
+castEnum = toEnum . fromEnum
+
+foreign import ccall "static b64.h b64u_enc_part"
+    c_b64u_enc_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
+
+foreign import ccall "static b64.h b64u_enc_final"
+    c_b64u_enc_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+foreign import ccall "static b64.h b64u_dec_part"
+    c_b64u_dec_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt
+
+foreign import ccall "static b64.h b64u_dec_final"
+    c_b64u_dec_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+b64u_encode_part :: BS.ByteString -> (BS.ByteString, BS.ByteString)
+b64u_encode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 3 * 4
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                c_b64u_enc_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer outBuf (castEnum outLen) (free outBuf)
+                return (outBs, remBs)
+
+b64u_encode_final :: BS.ByteString -> Maybe BS.ByteString
+b64u_encode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 4
+    alloca $ \ pOutLen -> do
+        r <- c_b64u_enc_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+b64url_decode_part :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) (BS.ByteString, BS.ByteString)
+b64url_decode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 4 * 3
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                r <- c_b64u_dec_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                if r == 0
+                    then return $ Right (outBs, remBs)
+                    else return $ Left (outBs, remBs)
+
+b64url_decode_final :: BS.ByteString -> Maybe BS.ByteString
+b64url_decode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 3
+    alloca $ \ pOutLen -> do
+        r <- c_b64u_dec_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+encode :: BS.ByteString -> BS.ByteString
+encode bs = let
+        (first, rest) = b64u_encode_part bs
+        Just final = b64u_encode_final rest
+    in first `BS.append` final
+
+decode :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+decode bs = either
+    Left
+    (\ (first, rest) ->
+        maybe
+            (Left (first, rest))
+            (\ fin -> Right (first `BS.append` fin))
+            (b64url_decode_final rest))
+    (b64url_decode_part bs)
diff --git a/src/Codec/Binary/Base85.hs b/src/Codec/Binary/Base85.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Base85.hs
@@ -0,0 +1,182 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module: Codec.Binary.Base85
+-- Copyright: (c) 2012 Magnus Therning
+-- License: BSD3
+--
+-- Implemented as described at <http://en.wikipedia.org/wiki/Ascii85>.
+module Codec.Binary.Base85
+   ( b85_encode_part
+   , b85_encode_final
+   , b85_decode_part
+   , b85_decode_final
+   , encode
+   , decode
+   ) where
+
+import qualified Data.ByteString as BS
+import Foreign
+import Foreign.C.Types
+import System.IO.Unsafe as U
+import Data.ByteString.Unsafe
+
+castEnum :: (Enum a, Enum b) => a -> b
+castEnum = toEnum . fromEnum
+
+foreign import ccall "static b85.h b85_enc_part"
+    c_b85_enc_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
+
+foreign import ccall "static b85.h b85_enc_final"
+    c_b85_enc_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+foreign import ccall "static b85.h b85_dec_part"
+    c_b85_dec_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt
+
+foreign import ccall "static b85.h b85_dec_final"
+    c_b85_dec_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+-- | Encoding function.
+--
+-- Encodes as large a part as possible of the indata.
+--
+-- >>> b85_encode_part $ Data.ByteString.Char8.pack "foobar"
+-- ("AoDTs","ar")
+--
+-- It supports special handling of both all-zero groups and all-space groups.
+--
+-- >>> b85_encode_part $ Data.ByteString.Char8.pack "    "
+-- ("y", "")
+-- >>> b85_encode_part $ Data.ByteString.Char8.pack "\0\0\0\0"
+-- ("z", "")
+b85_encode_part :: BS.ByteString -> (BS.ByteString, BS.ByteString)
+b85_encode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 4 * 5
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                c_b85_enc_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return (outBs, remBs)
+
+-- | Encoding function for the final block.
+--
+-- >>> b85_encode_final $ Data.ByteString.Char8.pack "ar"
+-- Just "@<)"
+b85_encode_final :: BS.ByteString -> Maybe BS.ByteString
+b85_encode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 5
+    alloca $ \ pOutLen -> do
+        r <- c_b85_enc_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Decoding function.
+--
+-- Decode as large a portion of the input as possible.
+--
+-- >>> b85_decode_part $ Data.ByteString.Char8.pack "AoDTs"
+-- Right ("foob","")
+-- >>> b85_decode_part $ Data.ByteString.Char8.pack "AoDTs@<)"
+-- Right ("foob","@<)")
+-- >>> b85_decode_part $ Data.ByteString.Char8.pack "@<)"
+-- Right ("","@<)")
+--
+-- At least 512 bytes of data is allocated for the output, but because of the
+-- special handling of all-zero and all-space groups it is possible that the
+-- space won't be enough.  (To be sure to always fit the output one would have
+-- to allocate 5 times the length of the input.  It seemed a good trade-off to
+-- sometimes have to call the function more than once instead.)
+--
+-- >>> either snd snd $ b85_decode_part $ Data.ByteString.Char8.pack $ Prelude.take 129 $ repeat 'y'
+-- "y"
+b85_decode_part :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) (BS.ByteString, BS.ByteString)
+b85_decode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = max 512 $ inLen `div` 5 * 4
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                r <- c_b85_dec_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                if r == 0
+                    then return $ Right (outBs, remBs)
+                    else return $ Left (outBs, remBs)
+
+-- | Decoding function for the final block.
+--
+-- >>> b85_decode_final $ Data.ByteString.Char8.pack "@<)"
+-- Just "ar"
+-- >>> b85_decode_final $ Data.ByteString.Char8.pack ""
+-- Just ""
+-- >>> b85_decode_final $ Data.ByteString.Char8.pack "AoDTs"
+-- Nothing
+b85_decode_final :: BS.ByteString -> Maybe BS.ByteString
+b85_decode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 4
+    alloca $ \ pOutLen -> do
+        r <- c_b85_dec_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Convenience function that combines 'b85_encode_part' and
+-- 'b85_encode_final' to encode a complete string.
+--
+-- >>> encode  $ Data.ByteString.Char8.pack "foob"
+-- "AoDTs"
+-- >>> encode  $ Data.ByteString.Char8.pack "foobar"
+-- "AoDTs@<)"
+encode :: BS.ByteString -> BS.ByteString
+encode bs = let
+        (first, rest) = b85_encode_part bs
+        Just final = b85_encode_final rest
+    in first `BS.append` final
+
+-- | Convenience function that combines 'b85_decode_part' and
+-- 'b85_decode_final' to decode a complete string.
+--
+-- >>> decode  $ Data.ByteString.Char8.pack "AoDTs"
+-- "foob"
+-- >>> encode  $ Data.ByteString.Char8.pack "AoDTs@<)"
+-- "foobar"
+decode :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+decode bs = let
+        iterateDecode bss rem = case b85_decode_part rem of
+            Right (d, r) ->
+                if BS.null d
+                    then Right (BS.concat (reverse bss), r)
+                    else iterateDecode (d : bss) r
+            Left (d, r) -> Left (BS.concat $ reverse $ d : bss, r)
+
+        handleFinal a@(first, rest) = maybe
+            (Left a)
+            (\ final -> Right (first `BS.append` final))
+            (b85_decode_final rest)
+
+    in either
+            Left
+            handleFinal
+            (iterateDecode [] bs)
diff --git a/src/Codec/Binary/QuotedPrintable.hs b/src/Codec/Binary/QuotedPrintable.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/QuotedPrintable.hs
@@ -0,0 +1,121 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module: Codec.Binary.QuotedPrintable
+-- Copyright: (c) 2012 Magnus Therning
+-- License: BSD3
+--
+-- Implementation of Quoted-Printable based on RFC 2045
+-- (<http://tools.ietf.org/html/rfc2045>).
+--
+-- This encoding encodes /everything/ that is passed in, it will not try to
+-- guess the native line ending for your architecture.  In other words, if you
+-- are using this to encode text you need to split it into separate lines
+-- before encoding.
+module Codec.Binary.QuotedPrintable
+    ( qp_enc
+    , qp_dec
+    , encode
+    , decode
+    ) where
+
+import Data.List
+import Foreign
+import Foreign.C.Types
+import System.IO.Unsafe as U
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Unsafe as BSU
+
+castEnum :: (Enum a, Enum b) => a -> b
+castEnum = toEnum . fromEnum
+
+foreign import ccall "static qp.h qp_enc"
+    c_qp_enc :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
+
+foreign import ccall "static qp.h qp_dec"
+    c_qp_dec :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt
+
+-- | Encoding function.
+--
+-- This function allocates enough space to hold twice the size of the indata
+-- (or at least 512 bytes) and then encodes as much as possible of the indata.
+-- That means there is a risk that the encoded data won't fit and in that case
+-- the second part of the pair contains the remainder of the indata.
+--
+-- >>> qp_enc $ Data.ByteString.Char8.pack "="
+-- ("=3D","")
+-- >>> snd $ qp_enc $ Data.ByteString.Char8.pack $ Data.List.take 171 $ repeat '='
+-- "="
+qp_enc :: BS.ByteString -> (BS.ByteString, BS.ByteString)
+qp_enc bs = U.unsafePerformIO $ BSU.unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutBuf = max 512 (2 * inLen)
+    outBuf <- mallocBytes maxOutBuf
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutBuf)
+                c_qp_enc (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- BSU.unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return (outBs, remBs)
+
+-- | Decoding function.
+--
+-- >>> qp_dec $ Data.ByteString.Char8.pack "foobar"
+-- Right "foobar"
+-- >>> qp_dec $ Data.ByteString.Char8.pack "1=20+=201=20=3D=202"
+-- Right "1 + 1 = 2"
+--
+-- The input data is allowed to use lowercase letters in the hexadecimal
+-- representation of an octets value, even though the standard says that only
+-- uppercase letters may be used:
+--
+-- >>> qp_dec $ Data.ByteString.Char8.pack "=3D"
+-- Right "="
+-- >>> qp_dec $ Data.ByteString.Char8.pack "=3d"
+-- Right "="
+--
+-- It also allows the input to encode _all_ octets in the hexadecimal
+-- representation:
+--
+-- >>> qp_dec $ Data.ByteString.Char8.pack "=20!"
+-- Right " !"
+-- >>> qp_dec $ Data.ByteString.Char8.pack "=20=21"
+-- Right " !"
+--
+-- A @Left@ value is only ever returned on decoding errors.
+--
+-- >>> qp_dec $ Data.ByteString.Char8.pack "=2"
+-- Left ("","=2")
+-- >>> qp_dec $ Data.ByteString.Char8.pack "=2g"
+-- Left ("","=2g")
+qp_dec :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+qp_dec bs = U.unsafePerformIO $ BSU.unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes inLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum inLen)
+                r <- c_qp_dec (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- BSU.unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                if r == 0
+                    then return $ Right outBs
+                    else return $ Left (outBs, remBs)
+
+-- | Convenient function that calls 'qp_enc' repeatedly until the whole input
+-- data is encoded.
+encode :: BS.ByteString -> BS.ByteString
+encode = BS.concat . takeWhile (not . BS.null) . unfoldr (Just . qp_enc)
+
+-- | A synonym for 'qp_dec'.
+decode :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+decode = qp_dec
diff --git a/src/Codec/Binary/Uu.hs b/src/Codec/Binary/Uu.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Uu.hs
@@ -0,0 +1,198 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module: Codec.Binary.Uu
+-- Copyright: (c) 2012 Magnus Therning
+-- License: BSD3
+--
+-- Uuencoding is notoriously badly specified.  This implementation aims at
+-- being compatible with the GNU Sharutils
+-- (<http://www.gnu.org/software/sharutils/>).
+--
+-- Just like Base64 encoding uuencoding expands blocks of 3 bytes into blocks
+-- of 4 bytes.  There is however no well defined ending to a piece of encoded
+-- data, instead uuencoded data is commonly transferred linewise where each
+-- line is prepended with the length of the data in the line.
+--
+-- This module currently only deals with the encoding.  Chopping the encoded
+-- data into lines, and unchopping lines into encoded data is left as an
+-- exercise to the reader.  (Patches are welcome.)
+module Codec.Binary.Uu
+    ( uu_encode_part
+    , uu_encode_final
+    , uu_decode_part
+    , uu_decode_final
+    , encode
+    , decode
+    ) where
+
+import Data.ByteString.Unsafe
+import Foreign
+import Foreign.C.Types
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+import System.IO.Unsafe as U
+import qualified Data.ByteString as BS
+
+castEnum :: (Enum a, Enum b) => a -> b
+castEnum = toEnum . fromEnum
+
+foreign import ccall "static uu.h uu_enc_part"
+    c_uu_enc_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
+
+foreign import ccall "static uu.h uu_enc_final"
+    c_uu_enc_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+foreign import ccall "static uu.h uu_dec_part"
+    c_uu_dec_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt
+
+foreign import ccall "static uu.h uu_dec_final"
+    c_uu_dec_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+-- | Encoding function.
+--
+-- This function encodes as large a portion of the input as possible and
+-- returns the encoded part together with the remaining part.  Enough space is
+-- allocated for the encoding to make sure that the remaining part is less than
+-- 3 bytes long, which means it can be passed to 'uu_encode_final' as is.
+--
+-- >>> uu_encode_part $ Data.ByteString.Char8.pack "foo"
+-- ("9F]O","")
+-- >>> uu_encode_part $ Data.ByteString.Char8.pack "foob"
+-- ("9F]O","b")
+uu_encode_part :: BS.ByteString -> (BS.ByteString, BS.ByteString)
+uu_encode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 3 * 4
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                c_uu_enc_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer outBuf (castEnum outLen) (free outBuf)
+                return (outBs, remBs)
+
+-- | Encoding function for the final block.
+--
+-- The final block has to have a size less than 3.
+--
+-- >>> uu_encode_final $ Data.ByteString.Char8.pack "r"
+-- Just "<@"
+--
+-- Trying to pass in too large a block result in failure:
+--
+-- >>> uu_encode_final $ Data.ByteString.Char8.pack "foo"
+-- Nothing
+uu_encode_final :: BS.ByteString -> Maybe BS.ByteString
+uu_encode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 4
+    alloca $ \ pOutLen -> do
+        r <- c_uu_enc_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Decoding function.
+--
+-- Decode as large a portion of the input as possible.  Enough data is
+-- allocated for the output to ensure that the remainder is less than 4 bytes
+-- in size.  Success result in a @Right@ value:
+--
+-- >>> uu_decode_part $ Data.ByteString.Char8.pack "9F]O"
+-- Right ("foo","")
+-- >>> uu_decode_part $ Data.ByteString.Char8.pack "9F]O8F$"
+-- Right ("foo","8F$")
+--
+-- Failures occur on bad input and result in a @Left@ value:
+--
+-- >>> uu_decode_part $ Data.ByteString.Char8.pack "9F 0"
+-- Left ("","9F 0")
+uu_decode_part :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) (BS.ByteString, BS.ByteString)
+uu_decode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 4 * 3
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                r <- c_uu_dec_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                if r == 0
+                    then return $ Right (outBs, remBs)
+                    else return $ Left (outBs, remBs)
+
+-- | Decoding function for the final block.
+--
+-- The final block has to have a size of 0 or 4:
+--
+-- >>> uu_decode_final $ Data.ByteString.Char8.pack "9F\\"
+-- Just "fo"
+-- >>> uu_decode_final $ Data.ByteString.Char8.pack ""
+-- Just ""
+-- >>> uu_decode_final $ Data.ByteString.Char8.pack "9F¬"
+-- Nothing
+--
+-- But it must be the encoding of a block that is less than 3 bytes:
+--
+-- >>> uu_decode_final $ encode $ Data.ByteString.Char8.pack "foo"
+-- Nothing
+uu_decode_final :: BS.ByteString -> Maybe BS.ByteString
+uu_decode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 3
+    alloca $ \ pOutLen -> do
+        r <- c_uu_dec_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Convenience function that combines 'uu_encode_part' and
+-- 'uu_encode_final' to encode a complete string.
+--
+-- >>> encode $ Data.ByteString.Char8.pack "foo"
+-- "9F]O"
+-- >>> encode $ Data.ByteString.Char8.pack "foobar"
+-- "9F]O8F%R"
+encode :: BS.ByteString -> BS.ByteString
+encode bs = let
+        (first, rest) = uu_encode_part bs
+        Just final = uu_encode_final rest
+    in first `BS.append` final
+
+-- | Convenience function that combines 'uu_decode_part' and
+-- 'uu_decode_final' to decode a complete string.
+--
+-- >>> decode $ Data.ByteString.Char8.pack "9F]O"
+-- Right "foo"
+-- >>> decode $ Data.ByteString.Char8.pack "9F]O8F%R"
+-- Right "foobar"
+--
+-- Failures when decoding returns the decoded part and the remainder:
+--
+-- >>> decode $ Data.ByteString.Char8.pack "9F]O8F¬R"
+-- Left ("foo","8F\172R")
+decode :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+decode bs = either
+    Left
+    (\ (first, rest) ->
+        maybe
+            (Left (first, rest))
+            (\ fin -> Right (first `BS.append` fin))
+            (uu_decode_final rest))
+    (uu_decode_part bs)
diff --git a/src/Codec/Binary/Xx.hs b/src/Codec/Binary/Xx.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Xx.hs
@@ -0,0 +1,155 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module: Codec.Binary.Xx
+-- Copyright: (c) 2012 Magnus Therning
+-- License: BSD3
+--
+-- Xxencoding is obsolete but still included for completeness.  Further
+-- information on the encoding can be found at
+-- <http://en.wikipedia.org/wiki/Xxencode>.  It should be noted that this
+-- implementation performs no padding.
+--
+-- This encoding is very similar to uuencoding, therefore further information
+-- regarding the functions can be found in the documentation of
+-- "Codec.Binary.Uu".
+module Codec.Binary.Xx
+    ( xx_encode_part
+    , xx_encode_final
+    , xx_decode_part
+    , xx_decode_final
+    , encode
+    , decode
+    ) where
+
+import Data.ByteString.Unsafe
+import Foreign
+import Foreign.C.Types
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+import System.IO.Unsafe as U
+import qualified Data.ByteString as BS
+
+castEnum :: (Enum a, Enum b) => a -> b
+castEnum = toEnum . fromEnum
+
+foreign import ccall "static uu.h xx_enc_part"
+    c_xx_enc_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
+
+foreign import ccall "static uu.h xx_enc_final"
+    c_xx_enc_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+foreign import ccall "static uu.h xx_dec_part"
+    c_xx_dec_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt
+
+foreign import ccall "static uu.h xx_dec_final"
+    c_xx_dec_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt
+
+-- | Encoding function.
+--
+-- >>> xx_encode_part $ Data.ByteString.Char8.pack "foo"
+-- ("Naxj","")
+-- >>> xx_encode_part $ Data.ByteString.Char8.pack "foob"
+-- ("Naxj","b")
+xx_encode_part :: BS.ByteString -> (BS.ByteString, BS.ByteString)
+xx_encode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 3 * 4
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                c_xx_enc_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer outBuf (castEnum outLen) (free outBuf)
+                return (outBs, remBs)
+
+-- | Encoding function for the final block.
+--
+-- >>> xx_encode_final $ Data.ByteString.Char8.pack "r"
+-- Just "QU"
+-- >>> xx_encode_final $ Data.ByteString.Char8.pack "foo"
+-- Nothing
+xx_encode_final :: BS.ByteString -> Maybe BS.ByteString
+xx_encode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 4
+    alloca $ \ pOutLen -> do
+        r <- c_xx_enc_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+-- | Decoding function.
+--
+-- >>> xx_decode_part $ Data.ByteString.Char8.pack "Naxj"
+-- Right ("foo","")
+-- >>> xx_decode_part $ Data.ByteString.Char8.pack "NaxjMa3"
+-- Right ("foo","Ma3")
+--
+-- >>> xx_decode_part $ Data.ByteString.Char8.pack "Na j"
+-- Left ("","Na J")
+xx_decode_part :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) (BS.ByteString, BS.ByteString)
+xx_decode_part bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = inLen `div` 4 * 3
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                r <- c_xx_dec_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                if r == 0
+                    then return $ Right (outBs, remBs)
+                    else return $ Left (outBs, remBs)
+
+-- | Decoding function for the final block.
+--
+-- >>> xx_decode_final $ Data.ByteString.Char8.pack "Naw"
+-- Just "fo"
+-- >>> xx_decode_final $ Data.ByteString.Char8.pack ""
+-- Just ""
+-- >>> xx_decode_final $ Data.ByteString.Char8.pack "Na "
+-- Nothing
+--
+-- >>> xx_decode_final $ encode $ Data.ByteString.Char8.pack "foo"
+-- Nothing
+xx_decode_final :: BS.ByteString -> Maybe BS.ByteString
+xx_decode_final bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes 3
+    alloca $ \ pOutLen -> do
+        r <- c_xx_dec_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen
+        if r == 0
+            then do
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf)
+                return $ Just outBs
+            else free outBuf >> return Nothing
+
+encode :: BS.ByteString -> BS.ByteString
+encode bs = let
+        (first, rest) = xx_encode_part bs
+        Just final = xx_encode_final rest
+    in first `BS.append` final
+
+decode :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+decode bs = either
+    Left
+    (\ (first, rest) ->
+        maybe
+            (Left (first, rest))
+            (\ fin -> Right (first `BS.append` fin))
+            (xx_decode_final rest))
+    (xx_decode_part bs)
diff --git a/src/Codec/Binary/Yenc.hs b/src/Codec/Binary/Yenc.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Yenc.hs
@@ -0,0 +1,97 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module: Codec.Binary.Yenc
+-- Copyright: (c) 2012 Magnus Therning
+-- License: BSD3
+--
+-- Implementation based on the specification found at
+-- <http://yence.sourceforge.net/docs/protocol/version1_3_draft.html>.
+module Codec.Binary.Yenc
+    ( y_enc
+    , y_dec
+    , encode
+    , decode
+    ) where
+
+import qualified Data.ByteString as BS
+import Foreign
+import Foreign.C.Types
+import System.IO.Unsafe as U
+import qualified Data.ByteString.Unsafe as BSU
+import Data.List
+
+castEnum :: (Enum a, Enum b) => a -> b
+castEnum = toEnum . fromEnum
+
+foreign import ccall "static yenc.h y_enc"
+    c_y_enc :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
+
+foreign import ccall "static yenc.h y_dec"
+    c_y_dec :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt
+
+-- | Encoding function.
+--
+-- This function allocates enough space to hold 20% more than the size of the
+-- indata (or at least 512 bytes) and then encodes as much as possible of the
+-- indata.  That means there is a risk that the encoded data won't fit and in
+-- that case the second part of the pair contains the remainder of the indata.
+--
+-- >>> y_enc $ Data.ByteString.Char8.pack "foobar"
+-- ("\144\153\153\140\139\156","")
+-- >>> snd $ y_enc $ Data.ByteString.Char8.pack $ Data.List.take 257 $ repeat '\x13'
+-- "\DC3"
+y_enc :: BS.ByteString -> (BS.ByteString, BS.ByteString)
+y_enc bs = U.unsafePerformIO $ BSU.unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    let maxOutLen = max 512 (ceiling $ (toRational inLen) * 1.2)
+    outBuf <- mallocBytes maxOutLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum maxOutLen)
+                c_y_enc (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- BSU.unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free outBuf)
+                return (outBs, remBs)
+
+-- | Decoding function.
+--
+-- >>> y_dec $ Data.ByteString.pack [144,153,153,140,139,156]
+-- Right "foobar"
+-- >>> y_dec $ Data.ByteString.Char8.pack "=}"
+-- Right "\DC3"
+--
+-- A @Left@ value is only ever returned on decoding errors.
+--
+-- >>> y_dec $ Data.ByteString.Char8.pack "="
+-- Left ("","=")
+y_dec :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+y_dec bs = U.unsafePerformIO $ BSU.unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do
+    outBuf <- mallocBytes inLen
+    alloca $ \ pOutLen ->
+        alloca $ \ pRemBuf ->
+            alloca $ \ pRemLen -> do
+                poke pOutLen (castEnum inLen)
+                _ <- c_y_dec (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen
+                outLen <- peek pOutLen
+                newOutBuf <- reallocBytes outBuf (castEnum outLen)
+                remBuf <- peek pRemBuf
+                remLen <- peek pRemLen
+                remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen)
+                outBs <- BSU.unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free outBuf)
+                if remLen == 0
+                    then return $ Right outBs
+                    else return $ Left (outBs, remBs)
+
+-- | Convenient function that calls 'y_enc' repeatedly until the whole input
+-- data is encoded.
+encode :: BS.ByteString -> BS.ByteString
+encode = BS.concat . takeWhile (not . BS.null) . unfoldr (Just . y_enc)
+
+-- | A synonym for 'y_dec'.
+decode :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString
+decode = y_dec
diff --git a/test-src/Main.hs b/test-src/Main.hs
new file mode 100644
--- /dev/null
+++ b/test-src/Main.hs
@@ -0,0 +1,278 @@
+{-# OPTIONS_GHC -XTemplateHaskell #-}
+
+-- Copyright: (c) Magnus Therning, 2012
+-- License: BSD3, found in the LICENSE file
+
+module Main where
+
+import Test.Framework
+import Test.Framework.TH
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Char8 as BSC
+
+import qualified Codec.Binary.Base16 as B16
+import qualified Codec.Binary.Base32 as B32
+import qualified Codec.Binary.Base32Hex as B32H
+import qualified Codec.Binary.Base64 as B64
+import qualified Codec.Binary.Base64Url as B64U
+import qualified Codec.Binary.Base85 as B85
+import qualified Codec.Binary.QuotedPrintable as QP
+import qualified Codec.Binary.Uu as Uu
+import qualified Codec.Binary.Xx as Xx
+import qualified Codec.Binary.Yenc as Y
+
+-- {{{1 base16
+case_b16_enc_foobar = do
+    BSC.empty               @=? B16.encode BSC.empty
+    BSC.pack "66"           @=? B16.encode (BSC.pack "f")
+    BSC.pack "666F"         @=? B16.encode (BSC.pack "fo")
+    BSC.pack "666F6F"       @=? B16.encode (BSC.pack "foo")
+    BSC.pack "666F6F62"     @=? B16.encode (BSC.pack "foob")
+    BSC.pack "666F6F6261"   @=? B16.encode (BSC.pack "fooba")
+    BSC.pack "666F6F626172" @=? B16.encode (BSC.pack "foobar")
+
+case_b16_dec_foobar = do
+    Right BS.empty                            @=? B16.decode BS.empty
+    Right (BSC.pack "f")      @=? B16.decode (BSC.pack "66")
+    Right (BSC.pack "fo")     @=? B16.decode (BSC.pack "666F")
+    Right (BSC.pack "foo")    @=? B16.decode (BSC.pack "666F6F")
+    Right (BSC.pack "foob")   @=? B16.decode (BSC.pack "666F6F62")
+    Right (BSC.pack "fooba")  @=? B16.decode (BSC.pack "666F6F6261")
+    Right (BSC.pack "foobar") @=? B16.decode (BSC.pack "666F6F626172")
+
+case_b16_dec_failure = do
+    -- odd number of input bytes
+    (Left (BSC.pack "fooba", BS.pack [55])) @=? (B16.decode $ BS.pack [54,54,54,70,54,70,54,50,54,49,55])
+
+-- {{{1 base32
+case_b32_enc_foobar = do
+    BSC.empty                   @=? B32.encode BSC.empty
+    BSC.pack "MY======"         @=? B32.encode (BSC.pack "f")
+    BSC.pack "MZXQ===="         @=? B32.encode (BSC.pack "fo")
+    BSC.pack "MZXW6==="         @=? B32.encode (BSC.pack "foo")
+    BSC.pack "MZXW6YQ="         @=? B32.encode (BSC.pack "foob")
+    BSC.pack "MZXW6YTB"         @=? B32.encode (BSC.pack "fooba")
+    BSC.pack "MZXW6YTBOI======" @=? B32.encode (BSC.pack "foobar")
+
+case_b32_dec_foobar = do
+    Right BS.empty            @=? B32.decode BS.empty
+    Right (BSC.pack "f")      @=? B32.decode (BSC.pack "MY======")
+    Right (BSC.pack "fo")     @=? B32.decode (BSC.pack "MZXQ====")
+    Right (BSC.pack "foo")    @=? B32.decode (BSC.pack "MZXW6===")
+    Right (BSC.pack "foob")   @=? B32.decode (BSC.pack "MZXW6YQ=")
+    Right (BSC.pack "fooba")  @=? B32.decode (BSC.pack "MZXW6YTB")
+    Right (BSC.pack "foobar") @=? B32.decode (BSC.pack "MZXW6YTBOI======")
+
+case_b32_dec_failures = do
+    -- illegal char
+    Left (BSC.empty, BSC.pack "M=XW6YTB") @=? (B32.b32_decode_part $ BSC.pack "M=XW6YTB")
+    -- full block
+    Nothing @=? (B32.b32_decode_final $ BSC.pack "MZXW6YTB")
+    -- too short
+    Nothing @=? (B32.b32_decode_final $ BSC.pack "MZXW6Y=")
+
+-- {{{1 base32hex
+case_b32h_enc_foobar = do
+    BSC.empty                   @=? B32H.encode BS.empty
+    BSC.pack "CO======"         @=? B32H.encode (BSC.pack "f")
+    BSC.pack "CPNG===="         @=? B32H.encode (BSC.pack "fo")
+    BSC.pack "CPNMU==="         @=? B32H.encode (BSC.pack "foo")
+    BSC.pack "CPNMUOG="         @=? B32H.encode (BSC.pack "foob")
+    BSC.pack "CPNMUOJ1"         @=? B32H.encode (BSC.pack "fooba")
+    BSC.pack "CPNMUOJ1E8======" @=? B32H.encode (BSC.pack "foobar")
+
+case_b32h_dec_foobar = do
+    Right BS.empty            @=? B32H.decode BS.empty
+    Right (BSC.pack "f") @=? B32H.decode (BSC.pack "CO======")
+    Right (BSC.pack "fo") @=? B32H.decode (BSC.pack "CPNG====")
+    Right (BSC.pack "foo") @=? B32H.decode (BSC.pack "CPNMU===")
+    Right (BSC.pack "foob") @=? B32H.decode (BSC.pack "CPNMUOG=")
+    Right (BSC.pack "fooba") @=? B32H.decode (BSC.pack "CPNMUOJ1")
+    Right (BSC.pack "foobar") @=? B32H.decode (BSC.pack "CPNMUOJ1E8======")
+
+case_b32h_dec_failures = do
+    --  illegal char
+    Left (BS.empty, BSC.pack "C=NMUOJ1") @=? (B32H.b32h_decode_part $ BSC.pack "C=NMUOJ1")
+    -- full block
+    Nothing @=? (B32H.b32h_decode_final $ BSC.pack "CPNMUOJ1")
+    -- too short
+    Nothing @=? (B32H.b32h_decode_final $ BSC.pack "CPNMUO=")
+
+-- {{{1 base64
+case_b64_enc_foobar = do
+    BSC.empty           @=? B64.encode BSC.empty
+    BSC.pack "Zg=="     @=? B64.encode (BSC.pack "f")
+    BSC.pack "Zm8="     @=? B64.encode (BSC.pack "fo")
+    BSC.pack "Zm9v"     @=? B64.encode (BSC.pack "foo")
+    BSC.pack "Zm9vYg==" @=? B64.encode (BSC.pack "foob")
+    BSC.pack "Zm9vYmE=" @=? B64.encode (BSC.pack "fooba")
+    BSC.pack "Zm9vYmFy" @=? B64.encode (BSC.pack "foobar")
+
+case_b64_enc_specials = do
+    -- /++/
+    BSC.pack "/++/" @=? B64.encode (BS.pack [255,239,191])
+
+case_b64_dec_foobar = do
+    Right BSC.empty           @=? B64.decode BSC.empty
+    Right (BSC.pack "f")      @=? B64.decode (BSC.pack "Zg==")
+    Right (BSC.pack "fo")     @=? B64.decode (BSC.pack "Zm8=")
+    Right (BSC.pack "foo")    @=? B64.decode (BSC.pack "Zm9v")
+    Right (BSC.pack "foob")   @=? B64.decode (BSC.pack "Zm9vYg==")
+    Right (BSC.pack "fooba")  @=? B64.decode (BSC.pack "Zm9vYmE=")
+    Right (BSC.pack "foobar") @=? B64.decode (BSC.pack "Zm9vYmFy")
+
+case_b64_dec_specials = do
+    -- /++/
+    Right (BS.pack [255,239,191]) @=? B64.decode (BSC.pack "/++/")
+
+-- {{{1 base64url
+case_b64u_enc_foobar = do
+    BSC.empty           @=? B64U.encode BSC.empty
+    BSC.pack "Zg=="     @=? B64U.encode (BSC.pack "f")
+    BSC.pack "Zm8="     @=? B64U.encode (BSC.pack "fo")
+    BSC.pack "Zm9v"     @=? B64U.encode (BSC.pack "foo")
+    BSC.pack "Zm9vYg==" @=? B64U.encode (BSC.pack "foob")
+    BSC.pack "Zm9vYmE=" @=? B64U.encode (BSC.pack "fooba")
+    BSC.pack "Zm9vYmFy" @=? B64U.encode (BSC.pack "foobar")
+
+case_b64u_enc_specials = do
+    -- _--_
+    BSC.pack "_--_" @=? (B64U.encode $ BS.pack [255,239,191])
+
+case_b64u_dec_foobar = do
+    Right BSC.empty           @=? B64U.decode BSC.empty
+    Right (BSC.pack "f")      @=? B64U.decode (BSC.pack "Zg==")
+    Right (BSC.pack "fo")     @=? B64U.decode (BSC.pack "Zm8=")
+    Right (BSC.pack "foo")    @=? B64U.decode (BSC.pack "Zm9v")
+    Right (BSC.pack "foob")   @=? B64U.decode (BSC.pack "Zm9vYg==")
+    Right (BSC.pack "fooba")  @=? B64U.decode (BSC.pack "Zm9vYmE=")
+    Right (BSC.pack "foobar") @=? B64U.decode (BSC.pack "Zm9vYmFy")
+
+case_b64u_dec_specials = do
+    -- _--_
+    Right (BS.pack [255,239,191]) @=? B64U.decode (BSC.pack "_--_")
+
+-- {{{1 base85
+case_b85_enc_foobar = do
+    BS.empty @=? B85.encode BS.empty
+    BSC.pack "Ac"       @=? B85.encode (BSC.pack "f")
+    BSC.pack "Ao@"      @=? B85.encode (BSC.pack "fo")
+    BSC.pack "AoDS"     @=? B85.encode (BSC.pack "foo")
+    BSC.pack "AoDTs"    @=? B85.encode (BSC.pack "foob")
+    BSC.pack "AoDTs@/"  @=? B85.encode (BSC.pack "fooba")
+    BSC.pack "AoDTs@<)" @=? B85.encode (BSC.pack "foobar")
+
+case_b85_enc_specials = do
+    -- all zero
+    BSC.pack "z" @=? B85.encode (BS.pack [0,0,0,0])
+    -- all space
+    BSC.pack "y" @=? B85.encode (BS.pack [32,32,32,32])
+    -- double special
+    BSC.pack "yz" @=? B85.encode (BS.pack [32,32,32,32,0,0,0,0])
+
+case_b85_dec_foobar = do
+    -- foobar
+    Right BS.empty            @=? B85.decode BS.empty
+    Right (BSC.pack "f")      @=? B85.decode (BSC.pack "Ac")
+    Right (BSC.pack "fo")     @=? B85.decode (BSC.pack "Ao@")
+    Right (BSC.pack "foo")    @=? B85.decode (BSC.pack "AoDS")
+    Right (BSC.pack "foob")   @=? B85.decode (BSC.pack "AoDTs")
+    Right (BSC.pack "fooba")  @=? B85.decode (BSC.pack "AoDTs@/")
+    Right (BSC.pack "foobar") @=? B85.decode (BSC.pack "AoDTs@<)")
+
+case_b85_dec_specials = do
+    -- all zero
+    Right (BS.pack [0,0,0,0]) @=? B85.decode (BSC.pack "z")
+    -- all space
+    Right (BS.pack [32,32,32,32]) @=? B85.decode (BSC.pack "y")
+    -- double special
+    Right (BS.pack [32,32,32,32,0,0,0,0]) @=? B85.decode (BSC.pack "yz")
+
+-- {{{1 quoted printable
+case_qp_enc_foobar = do
+    BS.empty @=? QP.encode BS.empty
+    BSC.pack "foobar" @=? QP.encode (BSC.pack "foobar")
+
+case_qp_dec_foobar = do
+    Right BS.empty @=? QP.decode BS.empty
+    Right (BSC.pack "foobar") @=? QP.decode (BSC.pack "foobar")
+
+-- {{{1 uu
+case_uu_enc_foobar = do
+    BS.empty            @=? Uu.encode BS.empty
+    BSC.pack "9@"       @=? Uu.encode (BSC.pack "f")
+    BSC.pack "9F\\"     @=? Uu.encode (BSC.pack "fo")
+    BSC.pack "9F]O"     @=? Uu.encode (BSC.pack "foo")
+    BSC.pack "9F]O8@"   @=? Uu.encode (BSC.pack "foob")
+    BSC.pack "9F]O8F$"  @=? Uu.encode (BSC.pack "fooba")
+    BSC.pack "9F]O8F%R" @=? Uu.encode (BSC.pack "foobar")
+
+case_uu_dec_foobar = do
+    Right BS.empty            @=? Uu.decode BS.empty
+    Right (BSC.pack "f")      @=? Uu.decode (BSC.pack "9@")
+    Right (BSC.pack "fo")     @=? Uu.decode (BSC.pack "9F\\")
+    Right (BSC.pack "foo")    @=? Uu.decode (BSC.pack "9F]O")
+    Right (BSC.pack "foob")   @=? Uu.decode (BSC.pack "9F]O8@")
+    Right (BSC.pack "fooba")  @=? Uu.decode (BSC.pack "9F]O8F$")
+    Right (BSC.pack "foobar") @=? Uu.decode (BSC.pack "9F]O8F%R")
+
+-- {{{1 xx
+case_xx_enc_foobar = do
+    -- foobar
+    BS.empty            @=? Xx.encode BS.empty
+    BSC.pack "NU"       @=? Xx.encode (BSC.pack "f")
+    BSC.pack "Naw"      @=? Xx.encode (BSC.pack "fo")
+    BSC.pack "Naxj"     @=? Xx.encode (BSC.pack "foo")
+    BSC.pack "NaxjMU"   @=? Xx.encode (BSC.pack "foob")
+    BSC.pack "NaxjMa2"  @=? Xx.encode (BSC.pack "fooba")
+    BSC.pack "NaxjMa3m" @=? Xx.encode (BSC.pack "foobar")
+
+case_xx_dec_foobar = do
+    -- foobar
+    Right BS.empty            @=? Xx.decode BS.empty
+    Right (BSC.pack "f")      @=? Xx.decode (BSC.pack "NU")
+    Right (BSC.pack "fo")     @=? Xx.decode (BSC.pack "Naw")
+    Right (BSC.pack "foo")    @=? Xx.decode (BSC.pack "Naxj")
+    Right (BSC.pack "foob")   @=? Xx.decode (BSC.pack "NaxjMU")
+    Right (BSC.pack "fooba")  @=? Xx.decode (BSC.pack "NaxjMa2")
+    Right (BSC.pack "foobar") @=? Xx.decode (BSC.pack "NaxjMa3m")
+
+-- {{{1 yenc
+case_y_enc_foobar = do
+    BS.empty                          @=? Y.encode BS.empty
+    BS.pack [144]                     @=? Y.encode (BSC.pack "f")
+    BS.pack [144,153]                 @=? Y.encode (BSC.pack "fo")
+    BS.pack [144,153,153]             @=? Y.encode (BSC.pack "foo")
+    BS.pack [144,153,153,140]         @=? Y.encode (BSC.pack "foob")
+    BS.pack [144,153,153,140,139]     @=? Y.encode (BSC.pack "fooba")
+    BS.pack [144,153,153,140,139,156] @=? Y.encode (BSC.pack "foobar")
+
+case_y_enc_specials = do
+    -- expanded chars
+    BS.pack [61,64]  @=? Y.encode (BS.pack [214])
+    BS.pack [61,74]  @=? Y.encode (BS.pack [224])
+    BS.pack [61,77]  @=? Y.encode (BS.pack [227])
+    BS.pack [61,125] @=? Y.encode (BS.pack [19])
+
+case_y_dec_foobar = do
+    Right BS.empty            @=? Y.decode BS.empty
+    Right (BSC.pack "f")      @=? Y.decode (BS.pack [144])
+    Right (BSC.pack "fo")     @=? Y.decode (BS.pack [144,153])
+    Right (BSC.pack "foo")    @=? Y.decode (BS.pack [144,153,153])
+    Right (BSC.pack "foob")   @=? Y.decode (BS.pack [144,153,153,140])
+    Right (BSC.pack "fooba")  @=? Y.decode (BS.pack [144,153,153,140,139])
+    Right (BSC.pack "foobar") @=? Y.decode (BS.pack [144,153,153,140,139,156])
+
+case_y_dec_specials = do
+    -- expanded chars
+    Right (BS.pack [214]) @=? Y.decode (BS.pack [61,64])
+    Right (BS.pack [224]) @=? Y.decode (BS.pack [61,74])
+    Right (BS.pack [227]) @=? Y.decode (BS.pack [61,77])
+    Right (BS.pack [19])  @=? Y.decode (BS.pack [61,125])
+
+-- {{{1 tests & main
+tests = [$(testGroupGenerator)]
+
+main = defaultMain tests
