forked from couchbaselabs/iErl14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of github.com:couchbaselabs/iErl14
Conflicts: iErl14.xcodeproj/project.pbxproj
- Loading branch information
Showing
37 changed files
with
6,506 additions
and
30 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); |
Oops, something went wrong.