Permalink
Browse files

added code for nifs version in chapter 12

  • Loading branch information...
1 parent e7c3322 commit dd5d41fa8cf4ee05b38ab17b43be453e160b715f @richcarl richcarl committed with Eric Merritt Jul 22, 2010
@@ -0,0 +1,28 @@
+To build the Erlang code, run the following command:
+
+erlc -o ./ebin ./src/*.erl
+
+To build the C code using gcc, run the following:
+
+gcc -o ./priv/jp_nifs -fpic -shared -I$(OTPROOT)/erts-5.7.5/include -I$(OTPROOT)/lib/erl_interface-3.6.5/include -I$(YAJLROOT)/include -L$(OTPROOT)/lib/erl_interface-3.6.5/lib -L$(YAJLROOT)/lib ./c_src/jp_nifs.c -lyajl
+
+OTPROOT is typically /usr/lib/erlang. Check what your versions of erts and
+erl_interface are and that the include and lib directories exist and contain
+the expected header files and library files - not all Erlang distributions
+ship these development files in the basic installation package. YAJLROOT is
+wherever your YAJL installation is. If you built YAJL but did not do 'make
+install', this will be something like lloyd-yajl-1.0.9-0/build/yajl-1.0.9/.
+
+For the YAJL shared library to be loaded at runtime, you may need to set the
+environment variable LD_LIBRARY_PATH to ${YAJLROOT}/lib if you did not
+install YAJL it in a standard location.
+
+To run the program, first start Erlang like this:
+
+erl -pa ../json_parser/ebin
+
+Then, run the following in the Erlang shell:
+
+1> json_parser:parse_document(<<"[null,true,{\"int\":42,\"float\":3.14}]">>).
+{ok,{undefined,true,[{<<"int">>,42},{<<"float">>,3.14}]}}
+2>
@@ -0,0 +1,283 @@
+/*
+ Erlang NIF interface to the YAJL JSON parser
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <erl_nif.h>
+
+#include <yajl/yajl_parse.h>
+
+#define BUFSIZE 65536
+
+/* yajl callback prototypes */
+static int handle_null(void *ctx);
+static int handle_boolean(void *ctx, int boolVal);
+static int handle_integer(void *ctx, long integerVal);
+static int handle_double(void *ctx, double doubleVal);
+static int handle_string(void *ctx, const unsigned char *stringVal,
+ unsigned int stringLen);
+static int handle_map_key(void *ctx, const unsigned char *stringVal,
+ unsigned int stringLen);
+static int handle_start_map(void *ctx);
+static int handle_end_map(void *ctx);
+static int handle_start_array(void *ctx);
+static int handle_end_array(void *ctx);
+
+static yajl_callbacks callbacks = {
+ handle_null,
+ handle_boolean,
+ handle_integer, /* note: only handles long integers, not bignums */
+ handle_double,
+ NULL, /* any number - if defined, integer/double are not used */
+ handle_string,
+ handle_start_map,
+ handle_map_key,
+ handle_end_map,
+ handle_start_array,
+ handle_end_array
+};
+
+
+typedef struct container_t {
+ int count; /* number of elements */
+ int arraysz; /* size of elements array */
+ ERL_NIF_TERM *array; /* elements array */
+ struct container_t *next;
+} container_t;
+
+typedef struct {
+ ErlNifEnv *env; /* NIF environment */
+ int key; /* true if last term was a key */
+ container_t *c; /* innermost container */
+} state_t;
+
+
+static void *alloc_func(void *ctx, unsigned int sz)
+{
+ return enif_alloc((ErlNifEnv *)ctx, sz);
+}
+
+static void *realloc_func(void *ctx, void *ptr, unsigned int sz)
+{
+ return enif_realloc((ErlNifEnv *)ctx, ptr, sz);
+}
+
+static void free_func(void *ctx, void *ptr)
+{
+ enif_free((ErlNifEnv *)ctx, ptr);
+}
+
+static yajl_alloc_funcs alloc_funcs = {
+ alloc_func,
+ realloc_func,
+ free_func,
+ NULL /* must be initialized below */
+};
+
+
+static const char *parse_json(state_t *st, unsigned char *buf, size_t len)
+{
+ yajl_parser_config cfg = {
+ 1, /* allow comments */
+ 0 /* don't check UTF-8 */
+ };
+ yajl_handle yh;
+ yajl_status ys;
+ const char *err=NULL;
+ char errmsg[256];
+
+ alloc_funcs.ctx = st->env; /* will be passed to alloc funcs */
+ yh = yajl_alloc(&callbacks, &cfg, &alloc_funcs, st);
+ ys = yajl_parse(yh, buf, len);
+ if (ys == yajl_status_insufficient_data) {
+ ys = yajl_parse_complete(yh);
+ }
+ if (ys == yajl_status_insufficient_data) {
+ err = "unexpected end of document";
+ } else if (ys != yajl_status_ok) {
+ unsigned char *msg = yajl_get_error(yh, 0, NULL, 0);
+ strncpy(errmsg, (char *)msg, sizeof(errmsg)-1);
+ yajl_free_error(yh, msg);
+ errmsg[sizeof(errmsg)] = 0;
+ err = errmsg;
+ }
+ yajl_free(yh);
+ return err;
+}
+
+
+static ERL_NIF_TERM parse_document_1(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[])
+{
+ /* initialize the state, with a dummy top level container */
+ state_t st;
+ st.env = env; /* keep NIF environment in state */
+ st.key = 0;
+ ERL_NIF_TERM term;
+ container_t c = { 0, 1, &term, NULL };
+ st.c = &c;
+
+ ErlNifBinary bin;
+ if (argc != 1 || !enif_is_binary(env, argv[0]))
+ return enif_make_badarg(env);
+ if (!enif_inspect_binary(env, argv[0], &bin))
+ return enif_make_badarg(env);
+
+ const char *err;
+ if ((err = parse_json(&st, bin.data, bin.size)) != NULL) {
+ return enif_make_tuple2(env, enif_make_atom(env, "error"),
+ enif_make_string(env, err, ERL_NIF_LATIN1));
+ }
+ return enif_make_tuple2(env, enif_make_atom(env, "ok"), term);
+}
+
+
+static ErlNifFunc json_parser_NIFs[] = {
+ {"parse_document", 1, &parse_document_1}
+};
+
+ERL_NIF_INIT(json_parser, json_parser_NIFs, NULL, NULL, NULL, NULL);
+
+
+/*
+ * JSON Erlang json() representation
+ * ---- ----------------------------
+ * true 'true'
+ * false 'false'
+ * null 'undefined'
+ * number (integers and floats) number()
+ * string: "..." binary()
+ * array/list: [ value, ... ] {json()}
+ * map: { label: value, ... } [{binary(), json()}]
+ */
+
+static void add_element(state_t *st, ERL_NIF_TERM t)
+{
+ container_t *c = st->c;
+ if (c != NULL) {
+ if (c->count >= c->arraysz) {
+ c->arraysz *= 2;
+ c->array = enif_realloc(st->env, c->array, c->arraysz);
+ }
+ if (st->key) {
+ /* the previous stored element was a key, so replace the entry
+ with a key/value pair, and don't count it twice */
+ c->array[c->count-1] = enif_make_tuple2(st->env,
+ c->array[c->count-1],
+ t);
+ st->key = 0;
+ } else {
+ c->array[c->count] = t;
+ ++(c->count);
+ }
+ }
+}
+
+static int handle_null(void *ctx)
+{
+ state_t *st = (state_t *)ctx;
+ add_element(st, enif_make_atom(st->env, "undefined"));
+ return 1;
+}
+
+static int handle_boolean(void *ctx, int boolVal)
+{
+ state_t *st = (state_t *)ctx;
+ if (boolVal) {
+ add_element(st, enif_make_atom(st->env, "true"));
+ } else {
+ add_element(st, enif_make_atom(st->env, "false"));
+ }
+ return 1;
+}
+
+static int handle_integer(void *ctx, long integerVal)
+{
+ state_t *st = (state_t *)ctx;
+ add_element(st, enif_make_long(st->env, integerVal));
+ return 1;
+}
+
+static int handle_double(void *ctx, double doubleVal)
+{
+ state_t *st = (state_t *)ctx;
+ add_element(st, enif_make_double(st->env, doubleVal));
+ return 1;
+}
+
+static int handle_string(void *ctx, const unsigned char *stringVal,
+ unsigned int stringLen)
+{
+ state_t *st = (state_t *)ctx;
+ ErlNifBinary bin;
+ enif_alloc_binary(st->env, stringLen, &bin);
+ strncpy((char *)bin.data, (char *)stringVal, stringLen);
+ add_element(st, enif_make_binary(st->env, &bin));
+ return 1;
+}
+
+static int handle_map_key(void *ctx, const unsigned char *stringVal,
+ unsigned int stringLen)
+{
+ state_t *st = (state_t *)ctx;
+ ErlNifBinary bin;
+ enif_alloc_binary(st->env, stringLen, &bin);
+ strncpy((char *)bin.data, (char *)stringVal, stringLen);
+ add_element(st, enif_make_binary(st->env, &bin));
+ st->key = 1; /* note that the next term will be the value */
+ return 1;
+}
+
+static int handle_start(void *ctx, int array)
+{
+ state_t *st = (state_t *)ctx;
+ container_t *c = enif_alloc(st->env, sizeof(container_t));
+ /* link and initialize container struct */
+ c->next = st->c;
+ st->c = c;
+ c->count = 0;
+ c->arraysz = 32; /* initial term buffer size */
+ c->array = enif_alloc(st->env, c->arraysz);
+ return 1;
+}
+
+static int handle_start_map(void *ctx)
+{
+ return handle_start(ctx, 0);
+}
+
+static int handle_start_array(void *ctx)
+{
+ return handle_start(ctx, 1);
+}
+
+static int handle_end(void *ctx, int array)
+{
+ state_t *st = (state_t *)ctx;
+ container_t *c = st->c;
+ /* unlink container struct from state */
+ st->c = c->next;
+ /* create and add container term */
+ if (array) {
+ add_element(st, enif_make_tuple_from_array(st->env, c->array,
+ c->count));
+ } else {
+ add_element(st, enif_make_list_from_array(st->env, c->array,
+ c->count));
+ }
+ /* decallocate used container struct */
+ enif_free(st->env, c);
+ return 1;
+}
+
+static int handle_end_map(void *ctx)
+{
+ return handle_end(ctx, 0);
+}
+
+static int handle_end_array(void *ctx)
+{
+ return handle_end(ctx, 1);
+}
@@ -0,0 +1,4 @@
+edoc-info
+stylesheet.css
+erlang.png
+*.html
@@ -0,0 +1 @@
+*.beam
@@ -0,0 +1,8 @@
+%% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*-
+
+{application, json_parser,
+ [{description, "JSON parser (using plain ports)"},
+ {vsn, "0.1.0"},
+ {modules, [json_parser]},
+ {applications, [kernel, stdlib]}
+ ]}.
@@ -0,0 +1 @@
+parser
@@ -0,0 +1,25 @@
+%%%-------------------------------------------------------------------
+%%% @doc JSON parser user API.
+%%% @end
+%%%-------------------------------------------------------------------
+
+-module(json_parser).
+
+-export([init/0, parse_document/1]).
+
+-on_load(init/0).
+
+-define(APPNAME, json_parser).
+
+init() ->
+ case code:priv_dir(?APPNAME) of
+ {error, _} ->
+ error_logger:format("~w priv dir not found~n", [?APPNAME]),
+ exit(error);
+ PrivDir ->
+ erlang:load_nif(filename:join([PrivDir, "jp_nifs"]), 0)
+ end.
+
+%% @doc Parses a document given as a binary
+parse_document(Data) ->
+ erlang:error(nif_not_loaded). % help Dialyzer and friends

0 comments on commit dd5d41f

Please sign in to comment.