/* ex: ts=4 sw=4 et
|
|
*
|
|
* Copyright (c) 2011-2016 Sergey Urbanovich
|
|
* http://github.com/urbanserj/cbase64-erlang-nif
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "stdint.h"
|
|
#include "erl_nif.h"
|
|
|
|
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
|
|
|
|
#define TIMESLICE 10
|
|
#define SPEEDUP 30
|
|
#define REDUCTIONS 2000
|
|
#define ITER (REDUCTIONS * SPEEDUP / TIMESLICE)
|
|
|
|
|
|
static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
inline void encodeblock(const uint8_t in[3], uint8_t out[4], size_t len)
|
|
{
|
|
out[0] = cb64[ (in[0] >> 2) & 0x3f ];
|
|
out[1] = cb64[ ((in[0] << 4) + (--len ? in[1] >> 4 : 0)) & 0x3f ];
|
|
out[2] = len ? cb64[ ((in[1] << 2) + (--len ? in[2] >> 6 : 0)) & 0x3f ] : '=';
|
|
out[3] = len ? cb64[ in[2] & 0x3f ] : '=';
|
|
}
|
|
|
|
static ERL_NIF_TERM encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
ERL_NIF_TERM tdst, tsrc;
|
|
ErlNifBinary src, dst;
|
|
|
|
if ( enif_inspect_binary(env, argv[0], &src) ) {
|
|
tsrc = argv[0];
|
|
} else if ( enif_inspect_iolist_as_binary(env, argv[0], &src) ) {
|
|
tsrc = enif_make_binary(env, &src);
|
|
} else {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (src.size == 0) {
|
|
return argv[0];
|
|
}
|
|
|
|
uint8_t *data;
|
|
size_t size = (src.size + 2) / 3 * 4;
|
|
if (argc == 1) {
|
|
data = enif_make_new_binary(env, size, &tdst);
|
|
} else {
|
|
tdst = argv[1];
|
|
if ( !enif_inspect_binary(env, argv[1], &dst) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
data = dst.data;
|
|
}
|
|
|
|
int i = size / 4 - 1;
|
|
if (argc == 3) {
|
|
if ( !enif_get_int(env, argv[2], &i) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
} else {
|
|
/* last block */
|
|
encodeblock(src.data + i * 3, data + i * 4, src.size - i * 3);
|
|
i--;
|
|
}
|
|
|
|
int reductions = 0;
|
|
for (; i >= 0; i--) {
|
|
encodeblock(src.data + i * 3, data + i * 4, 3);
|
|
reductions += 3;
|
|
if (reductions >= ITER) {
|
|
if (enif_consume_timeslice(env, TIMESLICE)) {
|
|
ERL_NIF_TERM schargv[3] = {tsrc, tdst, enif_make_int(env, i)};
|
|
return enif_schedule_nif(env, "encode", 0, &encode, 3, schargv);
|
|
}
|
|
reductions = 0;
|
|
}
|
|
}
|
|
|
|
return tdst;
|
|
}
|
|
|
|
|
|
#define DECODE_ERROR 0xff
|
|
#define DECODE_OK 0x00
|
|
|
|
#define B64(_) \
|
|
( (_) >= 'A' && (_) <= 'Z' ? 25 - 'Z' + (_) : \
|
|
(_) >= 'a' && (_) <= 'z' ? 51 - 'z' + (_) : \
|
|
(_) >= '0' && (_) <= '9' ? 61 - '9' + (_) : \
|
|
(_) == '+' ? 62 : (_) ? 63 : DECODE_ERROR )
|
|
|
|
static const int cd64[256] = {
|
|
B64( 0), B64( 1), B64( 2), B64( 3), B64( 4), B64( 5), B64( 6), B64( 7),
|
|
B64( 8), B64( 9), B64( 10), B64( 11), B64( 12), B64( 13), B64( 14), B64( 15),
|
|
B64( 16), B64( 17), B64( 18), B64( 19), B64( 20), B64( 21), B64( 22), B64( 23),
|
|
B64( 24), B64( 25), B64( 26), B64( 27), B64( 28), B64( 29), B64( 30), B64( 31),
|
|
B64( 32), B64( 33), B64( 34), B64( 35), B64( 36), B64( 37), B64( 38), B64( 39),
|
|
B64( 40), B64( 41), B64( 42), B64( 43), B64( 44), B64( 45), B64( 46), B64( 47),
|
|
B64( 48), B64( 49), B64( 50), B64( 51), B64( 52), B64( 53), B64( 54), B64( 55),
|
|
B64( 56), B64( 57), B64( 58), B64( 59), B64( 60), B64( 61), B64( 62), B64( 63),
|
|
B64( 64), B64( 65), B64( 66), B64( 67), B64( 68), B64( 69), B64( 70), B64( 71),
|
|
B64( 72), B64( 73), B64( 74), B64( 75), B64( 76), B64( 77), B64( 78), B64( 79),
|
|
B64( 80), B64( 81), B64( 82), B64( 83), B64( 84), B64( 85), B64( 86), B64( 87),
|
|
B64( 88), B64( 89), B64( 90), B64( 91), B64( 92), B64( 93), B64( 94), B64( 95),
|
|
B64( 96), B64( 97), B64( 98), B64( 99), B64(100), B64(101), B64(102), B64(103),
|
|
B64(104), B64(105), B64(106), B64(107), B64(108), B64(109), B64(110), B64(111),
|
|
B64(112), B64(113), B64(114), B64(115), B64(116), B64(117), B64(118), B64(119),
|
|
B64(120), B64(121), B64(122), B64(123), B64(124), B64(125), B64(126), B64(127),
|
|
B64(128), B64(129), B64(130), B64(131), B64(132), B64(133), B64(134), B64(135),
|
|
B64(136), B64(137), B64(138), B64(139), B64(140), B64(141), B64(142), B64(143),
|
|
B64(144), B64(145), B64(146), B64(147), B64(148), B64(149), B64(150), B64(151),
|
|
B64(152), B64(153), B64(154), B64(155), B64(156), B64(157), B64(158), B64(159),
|
|
B64(160), B64(161), B64(162), B64(163), B64(164), B64(165), B64(166), B64(167),
|
|
B64(168), B64(169), B64(170), B64(171), B64(172), B64(173), B64(174), B64(175),
|
|
B64(176), B64(177), B64(178), B64(179), B64(180), B64(181), B64(182), B64(183),
|
|
B64(184), B64(185), B64(186), B64(187), B64(188), B64(189), B64(190), B64(191),
|
|
B64(192), B64(193), B64(194), B64(195), B64(196), B64(197), B64(198), B64(199),
|
|
B64(200), B64(201), B64(202), B64(203), B64(204), B64(205), B64(206), B64(207),
|
|
B64(208), B64(209), B64(210), B64(211), B64(212), B64(213), B64(214), B64(215),
|
|
B64(216), B64(217), B64(218), B64(219), B64(220), B64(221), B64(222), B64(223),
|
|
B64(224), B64(225), B64(226), B64(227), B64(228), B64(229), B64(230), B64(231),
|
|
B64(232), B64(233), B64(234), B64(235), B64(236), B64(237), B64(238), B64(239),
|
|
B64(240), B64(241), B64(242), B64(243), B64(244), B64(245), B64(246), B64(247),
|
|
B64(248), B64(249), B64(250), B64(251), B64(252), B64(253), B64(254), B64(255)
|
|
};
|
|
|
|
inline uint8_t decodeblock(const uint8_t in[4], uint8_t out[3])
|
|
{
|
|
int code;
|
|
size_t it = 0;
|
|
unsigned int val = 0;
|
|
|
|
for (;it < 4; ++it) {
|
|
if ( (code = cd64[in[it]]) == DECODE_ERROR )
|
|
return DECODE_ERROR;
|
|
val = (val << 6) + code;
|
|
}
|
|
|
|
out[0] = (val >> 16) & 0xff;
|
|
out[1] = (val >> 8) & 0xff;
|
|
out[2] = val & 0xff;
|
|
|
|
return DECODE_OK;
|
|
}
|
|
|
|
inline uint8_t decodeblock_tail(const uint8_t in[4], uint8_t out[3])
|
|
{
|
|
int code;
|
|
size_t it = 0;
|
|
unsigned int val = 0;
|
|
|
|
if ( in[0] == '=' || in[1] == '=' ||
|
|
(in[2] == '=' && in[3] != '=') )
|
|
return DECODE_ERROR;
|
|
|
|
for (;it < 4; ++it) {
|
|
val <<= 6;
|
|
if ( in[it] == '=' )
|
|
continue;
|
|
if ( (code = cd64[in[it]]) == DECODE_ERROR )
|
|
return DECODE_ERROR;
|
|
val += code;
|
|
}
|
|
|
|
out[0] = (val >> 16) & 0xff;
|
|
if ( in[2] != '=' )
|
|
out[1] = (val >> 8) & 0xff;
|
|
if ( in[3] != '=' )
|
|
out[2] = val & 0xff;
|
|
|
|
return DECODE_OK;
|
|
}
|
|
|
|
static ERL_NIF_TERM decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
ERL_NIF_TERM tdst, tsrc;
|
|
ErlNifBinary src, dst;
|
|
|
|
if ( enif_inspect_binary(env, argv[0], &src) ) {
|
|
tsrc = argv[0];
|
|
} else if ( enif_inspect_iolist_as_binary(env, argv[0], &src) ) {
|
|
tsrc = enif_make_binary(env, &src);
|
|
} else {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (src.size == 0) {
|
|
return argv[0];
|
|
}
|
|
|
|
if (src.size % 4 != 0) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
uint8_t *data;
|
|
size_t size =
|
|
src.size/4*3
|
|
- (src.data[src.size - 1] == '=' ? 1 : 0)
|
|
- (src.data[src.size - 2] == '=' ? 1 : 0);
|
|
if (argc == 1) {
|
|
data = enif_make_new_binary(env, size, &tdst);
|
|
} else {
|
|
tdst = argv[1];
|
|
if ( !enif_inspect_binary(env, argv[1], &dst) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
data = dst.data;
|
|
}
|
|
|
|
int i = src.size / 4 - 1;
|
|
if (argc == 3) {
|
|
if ( !enif_get_int(env, argv[2], &i) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
} else {
|
|
/* last block */
|
|
if ( decodeblock_tail(src.data + i*4, data + i*3) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
i--;
|
|
}
|
|
|
|
int reductions = 0;
|
|
for (; i >= 0; i--) {
|
|
if ( decodeblock(src.data + i*4, data + i*3) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
reductions += 4;
|
|
if (reductions >= ITER) {
|
|
if (enif_consume_timeslice(env, TIMESLICE)) {
|
|
ERL_NIF_TERM schargv[3] = {tsrc, tdst, enif_make_int(env, i)};
|
|
return enif_schedule_nif(env, "decode", 0, &decode, 3, schargv);
|
|
}
|
|
reductions = 0;
|
|
}
|
|
}
|
|
return tdst;
|
|
}
|
|
|
|
|
|
static ErlNifFunc nif_funcs[] =
|
|
{
|
|
{"encode", 1, encode},
|
|
{"decode", 1, decode}
|
|
};
|
|
ERL_NIF_INIT(cbase64, nif_funcs, NULL, NULL, NULL, NULL)
|