Permalink
Browse files

MB-6773 Follow new JSON rules

 * There are no longer reserved field prefixes.
 * We allow any JSON value, not just arrays or objects.

from_binary is also used mostly by the CAPI layer, in order to get a doc
record from a ID/Body pair, and should not bother compressing docs, as
they will immediately be decompressed anyway.

Change-Id: Ib1d1dbeffd0d05cd91c86078ec84cbd4410b05dc
Reviewed-on: http://review.couchbase.org/21324
Reviewed-by: Damien Katz <damien@couchbase.com>
Reviewed-by: Filipe David Borba Manana <fdmanana@gmail.com>
Tested-by: Aaron Miller <apage43@ninjawhale.com>
  • Loading branch information...
1 parent e16e611 commit 6b9fa5f115e675ba345bf5ffa17e57423efd86ba @apage43 apage43 committed with Farshid Ghods Oct 3, 2012
Showing with 108 additions and 262 deletions.
  1. +0 −1 src/couchdb/couch_db.hrl
  2. +9 −12 src/couchdb/couch_doc.erl
  3. +7 −246 src/ejson/decode.c
  4. +1 −1 src/ejson/ejson.c
  5. +2 −2 src/ejson/ejson.erl
  6. +88 −0 test/etap/033-doc-from-binary.t
  7. +1 −0 test/etap/Makefile.am
@@ -64,7 +64,6 @@
-define(CONTENT_META_JSON, 0).
-define(CONTENT_META_INVALID_JSON, 1).
--define(CONTENT_META_INVALID_JSON_KEY, 2).
-define(CONTENT_META_NON_JSON_MODE, 3).
-define(CONTENT_META_SNAPPY_COMPRESSED, (1 bsl 7)).
@@ -66,8 +66,6 @@ content_meta_to_memcached_meta(?CONTENT_META_JSON) ->
nil;
content_meta_to_memcached_meta(?CONTENT_META_INVALID_JSON) ->
<<"invalid_json">>;
-content_meta_to_memcached_meta(?CONTENT_META_INVALID_JSON_KEY) ->
- <<"invalid_key">>;
content_meta_to_memcached_meta(?CONTENT_META_NON_JSON_MODE) ->
<<"non-JSON mode">>;
content_meta_to_memcached_meta(_Other) ->
@@ -141,26 +139,25 @@ to_json_bin(Doc0)->
mk_json_doc_from_binary(<<?LOCAL_DOC_PREFIX, _/binary>> = Id, Value) ->
- case ejson:validate(Value, <<"_$">>) of
- {ok, JsonBinary} ->
- #doc{id=Id, body = JsonBinary};
+ case ejson:validate(Value) of
+ ok ->
+ #doc{id=Id, body = Value};
Error ->
throw(Error)
end;
mk_json_doc_from_binary(Id, Value) ->
- case ejson:validate(Value, <<"_$">>) of
+ % Docs should accept any JSON value, not just objs and arrays
+ % (this would be anything that is acceptable as a value in an array
+ case ejson:validate([<<"[">>, Value, <<"]">>]) of
{error, invalid_json} ->
#doc{id=Id, body = Value,
content_meta = ?CONTENT_META_INVALID_JSON};
- {error, private_field_set} ->
- #doc{id=Id, body = Value,
- content_meta = ?CONTENT_META_INVALID_JSON_KEY};
{error, garbage_after_value} ->
#doc{id=Id, body = Value,
content_meta = ?CONTENT_META_INVALID_JSON};
- {ok, JsonBinary} ->
- #doc{id=Id, body = couch_compress:compress(JsonBinary),
- content_meta = ?CONTENT_META_JSON bor ?CONTENT_META_SNAPPY_COMPRESSED}
+ ok ->
+ #doc{id=Id, body = Value,
+ content_meta = ?CONTENT_META_JSON}
end.
from_binary(Id, Value, _WantJson=true) ->
View
@@ -25,22 +25,13 @@ typedef struct {
ErlNifEnv* env;
} decode_ctx;
-typedef struct {
- unsigned int depth;
- ErlNifBinary private;
- ErlNifBinary bin;
- size_t fill_offset;
- int error;
-} validate_ctx;
-
#define ENV(ctx) ((decode_ctx*)ctx)->env
#define CONTINUE 1
#define CANCEL 0
#define NOERROR 0
#define ERR_MEMORY 1
-#define ERR_PRIVATE_MEMBER 2
static void * yajl_internal_malloc(void *ctx, size_t sz);
@@ -230,181 +221,6 @@ decoder_callbacks = {
};
-/****** validate/normalization functions ******/
-
-static int
-ensure_buffer(validate_ctx* vctx, unsigned int len) {
- validate_ctx* ctx = (validate_ctx*)vctx;
- if ((ctx->bin.size - ctx->fill_offset) < len)
- {
- if(!enif_realloc_binary_compat(ctx->env, &(ctx->bin),
- (ctx->bin.size * 2) + len))
- {
- return ERR_MEMORY;
- }
- }
- return NOERROR;
-}
-
-/* on success, always ensures one extra char available in out bin */
-static int
-fill_buffer(validate_ctx* ctx, const char* str, unsigned int len)
-{
- if (ctx->error || (ctx->error = ensure_buffer(ctx, len + 1)))
- return CANCEL;
- memcpy(ctx->bin.data + ctx->fill_offset, str, len);
- ctx->fill_offset += len;
- return CONTINUE;
-}
-
-static int
-validate_start_array(void* vctx)
-{
- validate_ctx* ctx = (validate_ctx*)vctx;
- ctx->depth++;
- if (ctx->error || (ctx->error = ensure_buffer(ctx, 1)))
- return CANCEL;
- ctx->bin.data[ctx->fill_offset++] = '[';
- return CONTINUE;
-}
-
-static int
-validate_end_array(void* vctx)
-{
- validate_ctx* ctx = (validate_ctx*)vctx;
- ctx->depth--;
- if (ctx->bin.data[ctx->fill_offset - 1] == ',')
- ctx->fill_offset--;
- if (ctx->error || (ctx->error = ensure_buffer(ctx, 2)))
- return CANCEL;
- ctx->bin.data[ctx->fill_offset++] = ']';
- ctx->bin.data[ctx->fill_offset++] = ',';
- return CONTINUE;
-}
-
-static int
-validate_start_map(void* vctx)
-{
- validate_ctx* ctx = (validate_ctx*)vctx;
- ctx->depth++;
- if (ctx->error || (ctx->error = ensure_buffer(ctx, 1)))
- return CANCEL;
- ctx->bin.data[ctx->fill_offset++] = '{';
- return CONTINUE;
-}
-
-static int
-validate_end_map(void* vctx)
-{
- validate_ctx* ctx = (validate_ctx*)vctx;
- ctx->depth--;
- if (ctx->bin.data[ctx->fill_offset - 1] == ',')
- ctx->fill_offset--;
- if (ctx->error || (ctx->error = ensure_buffer(ctx, 2)))
- return CANCEL;
- ctx->bin.data[ctx->fill_offset++] = '}';
- ctx->bin.data[ctx->fill_offset++] = ',';
- return CONTINUE;
-}
-
-static int
-validate_map_key(void* vctx, const unsigned char* data, unsigned int size)
-{
- validate_ctx* ctx = (validate_ctx*)vctx;
- if(size != 0 && ctx->depth == 1)
- {
- int i = ctx->private.size;
- while(i--)
- {
- if(data[0] == ctx->private.data[i])
- {
- ctx->error = ERR_PRIVATE_MEMBER;
- return CANCEL;
- }
- }
- }
- if (ctx->error || (ctx->error = ensure_buffer(ctx, 1)))
- return CANCEL;
- ctx->bin.data[ctx->fill_offset++] = '"';
- yajl_string_encode2(fill_buffer, vctx, data, size);
- if (ctx->error || (ctx->error = ensure_buffer(ctx, 2)))
- return CANCEL;
- ctx->bin.data[ctx->fill_offset++] = '"';
- ctx->bin.data[ctx->fill_offset++] = ':';
- return CONTINUE;
-}
-
-static int
-validate_number(void* vctx, const char* numstr, unsigned int size)
-{
- validate_ctx* ctx = (validate_ctx*)vctx;
- if (CANCEL == fill_buffer(vctx, numstr, size))
- return CANCEL;
- ctx->bin.data[ctx->fill_offset++] = ',';
- return CONTINUE;
-}
-
-static int
-validate_bool(void* vctx, int boolValue)
-{
- int ret;
- validate_ctx* ctx = (validate_ctx*)vctx;
- if (boolValue)
- {
- ret = fill_buffer(vctx, "true", 4);
- }
- else
- {
- ret = fill_buffer(vctx, "false", 5);
- }
- if (CANCEL == ret)
- return CANCEL;
- ctx->bin.data[ctx->fill_offset++] = ',';
- return CONTINUE;
-}
-
-static int
-validate_null(void* vctx)
-{
- validate_ctx* ctx = (validate_ctx*)vctx;
- if (CANCEL == fill_buffer(vctx, "null", 4))
- {
- return CANCEL;
- }
- ctx->bin.data[ctx->fill_offset++] = ',';
- return CONTINUE;
-}
-
-static int
-validate_string(void* vctx, const unsigned char* str, unsigned int size)
-{
- validate_ctx* ctx = (validate_ctx*)vctx;
- if (ctx->error || (ctx->error = ensure_buffer(ctx, 1)))
- return;
- ctx->bin.data[ctx->fill_offset++] = '"';
- yajl_string_encode2(fill_buffer, vctx, str, size);
- if (ctx->error || (ctx->error = ensure_buffer(ctx, 2)))
- return;
- ctx->bin.data[ctx->fill_offset++] = '"';
- ctx->bin.data[ctx->fill_offset++] = ',';
- return CONTINUE;
-}
-
-static yajl_callbacks
-validate_callbacks = {
- validate_null,
- validate_bool,
- NULL,
- NULL,
- validate_number,
- validate_string,
- validate_start_map,
- validate_map_key,
- validate_end_map,
- validate_start_array,
- validate_end_array
-};
-
static int
check_rest(unsigned char* data, unsigned int size, unsigned int used)
{
@@ -436,37 +252,18 @@ validate_doc(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ErlNifBinary json;
ERL_NIF_TERM ret;
- validate_ctx ctx;
- ctx.depth = 0;
- ctx.fill_offset = 0;
- ctx.error = 0;
-
- handle = yajl_alloc(&validate_callbacks, &conf, &allocfuncs, &ctx);
+ handle = yajl_alloc(NULL, &conf, &allocfuncs, NULL);
if(!enif_inspect_iolist_as_binary(env, argv[0], &json))
{
ret = enif_make_badarg(env);
goto done;
}
- if(!enif_inspect_iolist_as_binary(env, argv[1], &ctx.private))
- {
- ret = enif_make_badarg(env);
- goto done;
- }
- /* normalized json will usually be no bigger than input */
- if (!enif_alloc_binary_compat(env, json.size, &ctx.bin))
- {
- ret = enif_make_tuple(env, 2,
- enif_make_atom(env, "error"),
- enif_make_atom(env, "insufficient_memory"));
- goto done;
- }
-
status = yajl_parse(handle, json.data, json.size);
if(status == yajl_status_insufficient_data &&
- handle->bytesConsumed == json.size)
+ handle->bytesConsumed == json.size)
{
status = yajl_parse_complete(handle);
}
@@ -476,58 +273,22 @@ validate_doc(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
if(handle->bytesConsumed != json.size &&
check_rest(json.data, json.size, handle->bytesConsumed) == CANCEL)
{
- enif_release_binary_compat(env, &ctx.bin);
ret = enif_make_tuple(env, 2,
enif_make_atom(env, "error"),
enif_make_atom(env, "garbage_after_value")
);
}
else
{
- assert(ctx.error == NOERROR);
- if (ctx.fill_offset && ctx.bin.data[ctx.fill_offset - 1] == ',')
- {
- ctx.fill_offset--;
- }
- enif_realloc_binary_compat(env, &(ctx.bin), ctx.fill_offset);
- ret = enif_make_tuple(env, 2,
- enif_make_atom(env, "ok"),
- // make the binary term which transfers ownership
- enif_make_binary(env, &ctx.bin)
- );
+ ret = enif_make_atom(env, "ok");
}
}
else
{
- enif_release_binary_compat(env, &ctx.bin);
- if(status == yajl_status_client_canceled)
- {
- if (ctx.error == ERR_MEMORY)
- {
- ret = enif_make_tuple(env, 2,
- enif_make_atom(env, "error"),
- enif_make_atom(env, "insufficient_memory"));
- }
- else if (ctx.error == ERR_PRIVATE_MEMBER)
- {
- ret = enif_make_tuple(env, 2,
- enif_make_atom(env, "error"),
- enif_make_atom(env, "private_field_set")
- );
- }
- else
- {
- //wtf!
- abort();
- }
- }
- else
- {
- ret = enif_make_tuple(env, 2,
- enif_make_atom(env, "error"),
- enif_make_atom(env, "invalid_json")
- );
- }
+ ret = enif_make_tuple(env, 2,
+ enif_make_atom(env, "error"),
+ enif_make_atom(env, "invalid_json")
+ );
}
done:
View
@@ -26,7 +26,7 @@ static ErlNifFunc nif_funcs[] =
{
{"final_encode", 1, final_encode},
{"reverse_tokens", 1, reverse_tokens},
- {"validate", 2, validate_doc}
+ {"validate", 1, validate_doc}
};
ERL_NIF_INIT(ejson, nif_funcs, &on_load, &on_reload, &on_upgrade, NULL);
View
@@ -11,7 +11,7 @@
% the License.
-module(ejson).
--export([encode/1, decode/1, validate/2]).
+-export([encode/1, decode/1, validate/1]).
-on_load(init/0).
init() ->
@@ -144,5 +144,5 @@ reverse_tokens(_) ->
final_encode(_) ->
erlang:nif_error(ejson_nif_not_loaded).
-validate(_, _) ->
+validate(_) ->
erlang:nif_error(ejson_nif_not_loaded).
Oops, something went wrong.

0 comments on commit 6b9fa5f

Please sign in to comment.