Skip to content

Commit

Permalink
Merge branch 'master' of github.com:couchbaselabs/iErl14
Browse files Browse the repository at this point in the history
Conflicts:
	iErl14.xcodeproj/project.pbxproj
  • Loading branch information
snej committed Aug 1, 2011
2 parents 861b2a5 + 6842e8a commit 492b18a
Show file tree
Hide file tree
Showing 37 changed files with 6,506 additions and 30 deletions.
170 changes: 170 additions & 0 deletions iErl14.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/config.h
Expand Up @@ -425,7 +425,7 @@
/* #undef HAVE_SYS_EPOLL_H */

/* Define if you have <sys/event.h> header file. */
/* #undef HAVE_SYS_EVENT_H */
#define HAVE_SYS_EVENT_H 1

/* Define to 1 if you have the <sys/ioctl.h> header file. */
#define HAVE_SYS_IOCTL_H 1
Expand Down
308 changes: 308 additions & 0 deletions src/ejson/decode.c
@@ -0,0 +1,308 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "erl_nif.h"
#include "erl_nif_compat.h"
#include "yajl/yajl_parse.h"
#include "yajl/yajl_parser.h"
#include "yajl/yajl_lex.h"

typedef struct {
ERL_NIF_TERM head;
ErlNifEnv* env;
} decode_ctx;

#define ENV(ctxarg) (((decode_ctx*)ctxarg)->env)

#define CONTINUE 1
#define CANCEL 0


static ERL_NIF_TERM
make_error(yajl_handle handle, ErlNifEnv* env)
{
char* yajlError = (char*) yajl_get_error(handle, 0, NULL, 0);
ERL_NIF_TERM errMsg;

if(yajlError != NULL)
{
errMsg = enif_make_string(env, yajlError, ERL_NIF_LATIN1);
yajl_free_error(handle, (unsigned char*) yajlError);
}
else
{
errMsg = enif_make_string(env, "unknown parse error", ERL_NIF_LATIN1);
}

return enif_make_tuple(env, 2,
enif_make_atom(env, "error"),
enif_make_tuple(env, 2,
enif_make_uint(env, handle->bytesConsumed),
errMsg
)
);
}


static void
add_to_head(void* vctx, ERL_NIF_TERM newhead)
{
decode_ctx* ctx = (decode_ctx*)vctx;
ctx->head = enif_make_list_cell(ctx->env, newhead, ctx->head);
}

static int
decode_null(void* ctx)
{
add_to_head(ctx, enif_make_atom(ENV(ctx), "null"));
return CONTINUE;
}

static int
decode_boolean(void* ctx, int val)
{
add_to_head(ctx, enif_make_atom(ENV(ctx), val ? "true" : "false"));
return CONTINUE;
}

static int
decode_number(void * ctx, const char * numberVal, unsigned int numberLen)
{
// scan in the input to see if it's a float or int

int numberType = 0; // 0 means integer, 1 means float
unsigned int i;
ErlNifBinary bin;
int missingDot = 1;
unsigned int expPos;

for(i=0; i<numberLen; i++) {
switch (numberVal[i]) {
case '.':
missingDot = 0;
numberType = 1; // it's a float
goto loopend;
case 'E':
case 'e':
expPos = i;
numberType = 1; // it's a float
goto loopend;
}
}
loopend:
if ((numberType == 1) && missingDot)
{
if(!enif_alloc_binary_compat(ENV(ctx), numberLen + 2, &bin))
{
return CANCEL;
}
memcpy(bin.data, numberVal, expPos);
bin.data[expPos] = '.';
bin.data[expPos + 1] = '0';
memcpy(bin.data + expPos + 2, numberVal + expPos, numberLen - expPos);
}
else
{
if(!enif_alloc_binary_compat(ENV(ctx), numberLen, &bin))
{
return CANCEL;
}
memcpy(bin.data, numberVal, numberLen);
}
add_to_head(ctx, enif_make_tuple(ENV(ctx), 2,
enif_make_int(ENV(ctx), numberType),
enif_make_binary(ENV(ctx), &bin)));
return CONTINUE;
}



static int
decode_string(void* ctx, const unsigned char* data, unsigned int size)
{
ErlNifBinary bin;
if(!enif_alloc_binary_compat(ENV(ctx), size, &bin))
{
return CANCEL;
}
memcpy(bin.data, data, size);
add_to_head(ctx, enif_make_binary(ENV(ctx), &bin));
return CONTINUE;
}

static int
decode_start_array(void* ctx)
{
add_to_head(ctx, enif_make_int(ENV(ctx), 0));
return CONTINUE;
}


static int
decode_end_array(void* ctx)
{
add_to_head(ctx, enif_make_int(ENV(ctx), 1));
return CONTINUE;
}


static int
decode_start_map(void* ctx)
{
add_to_head(ctx, enif_make_int(ENV(ctx), 2));
return CONTINUE;
}


static int
decode_end_map(void* ctx)
{
add_to_head(ctx, enif_make_int(ENV(ctx), 3));
return CONTINUE;
}


static int
decode_map_key(void* ctx, const unsigned char* data, unsigned int size)
{
ErlNifBinary bin;
if(!enif_alloc_binary_compat(ENV(ctx), size, &bin))
{
return CANCEL;
}
memcpy(bin.data, data, size);
add_to_head(ctx, enif_make_tuple(ENV(ctx), 2,
enif_make_int(ENV(ctx), 3),
enif_make_binary(ENV(ctx), &bin)));
return CONTINUE;
}

static yajl_callbacks
decoder_callbacks = {
decode_null,
decode_boolean,
NULL,
NULL,
decode_number,
decode_string,
decode_start_map,
decode_map_key,
decode_end_map,
decode_start_array,
decode_end_array
};

static int
check_rest(unsigned char* data, unsigned int size, unsigned int used)
{
unsigned int i = 0;
for(i = used; i < size; i++)
{
switch(data[i])
{
case ' ':
case '\t':
case '\r':
case '\n':
continue;
default:
return CANCEL;
}
}

return CONTINUE;
}

ERL_NIF_TERM
reverse_tokens(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
decode_ctx ctx;
yajl_parser_config conf = {0, 1}; // No comments, check utf8
yajl_handle handle = yajl_alloc(&decoder_callbacks, &conf, NULL, &ctx);
yajl_status status;
unsigned int used;
ErlNifBinary bin;
ERL_NIF_TERM ret;

ctx.env = env;
ctx.head = enif_make_list_from_array(env, NULL, 0);

if(!enif_inspect_iolist_as_binary(env, argv[0], &bin))
{
ret = enif_make_badarg(env);
goto done;
}

status = yajl_parse(handle, bin.data, bin.size);
used = handle->bytesConsumed;

// Parsing something like "2.0" (without quotes) will
// cause a spurious semi-error. We add the extra size
// check so that "2008-20-10" doesn't pass.
if(status == yajl_status_insufficient_data && used == bin.size)
{
status = yajl_parse_complete(handle);
}

if(status == yajl_status_ok && used != bin.size)
{
if(check_rest(bin.data, bin.size, used) == CANCEL)
{
ret = enif_make_tuple(env, 2,
enif_make_atom(env, "error"),
enif_make_atom(env, "garbage_after_value")
);
goto done;
}
}

switch(status)
{
case yajl_status_ok:
ret = enif_make_tuple(env, 2, enif_make_atom(env, "ok"), ctx.head);
goto done;

case yajl_status_error:
ret = make_error(handle, env);
goto done;

case yajl_status_insufficient_data:
ret = enif_make_tuple(env, 2,
enif_make_atom(env, "error"),
enif_make_atom(env, "insufficient_data")
);
goto done;

case yajl_status_client_canceled:
/* the only time we do this is when we can't allocate a binary. */
ret = enif_make_tuple(env, 2,
enif_make_atom(env, "error"),
enif_make_atom(env, "insufficient_memory")
);
goto done;

default:
ret = enif_make_tuple(env, 2,
enif_make_atom(env, "error"),
enif_make_atom(env, "unknown")
);
goto done;
}

done:
if(handle != NULL) yajl_free(handle);
return ret;
}
30 changes: 30 additions & 0 deletions src/ejson/ejson.c
@@ -0,0 +1,30 @@
#include "erl_nif.h"

ERL_NIF_TERM final_encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM reverse_tokens(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);

int
ej_on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM info)
{
return 0;
}

int
ej_on_reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM info)
{
return 0;
}

int
ej_on_upgrade(ErlNifEnv* env, void** priv_data, void** old_data, ERL_NIF_TERM info)
{
return 0;
}

static ErlNifFunc nif_funcs[] =
{
{"final_encode", 1, final_encode},
{"reverse_tokens", 1, reverse_tokens}
};

ERL_NIF_INIT(ejson, nif_funcs, &ej_on_load, &ej_on_reload, &ej_on_upgrade, NULL);

0 comments on commit 492b18a

Please sign in to comment.