C-based memory friendly JSON parser based on Serge Zaitsev's JSMN (https://bitbucket.org/zserge/jsmn/wiki/Home)

Dependents:   _library_jsmn _library_jsmn _library_jsmn

JSMN

jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be easily integrated into resource-limited or embedded projects.

You can find more information about JSON format at json.org

Library sources are available at https://bitbucket.org/zserge/jsmn

The web page with some information about jsmn can be found at http://zserge.com/jsmn.html

Revision:
0:46575249ef23
Child:
1:70061827a9c8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jsmn.cpp	Tue May 06 09:33:30 2014 +0000
@@ -0,0 +1,370 @@
+/* C-based low-memory footprint JSON parser for mbed
+ * Based on Serge Zaitsev's JSMN https://bitbucket.org/zserge/jsmn/wiki/Home
+ * JSMN is distributed under MIT license.
+ *
+ * Copyright (c) 2014 YoongHM
+ *
+ * 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 <stdlib.h>
+
+#include "jsmn.h"
+
+
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *
+jsmn_alloc_token
+(jsmn_parser *parser
+,jsmntok_t   *tokens
+,size_t       num_tokens
+)
+{
+  jsmntok_t  *tok;
+  if (parser->toknext >= num_tokens)
+  {
+    return NULL;
+  }
+  tok = &tokens[parser->toknext++];
+  tok->start = tok->end = -1;
+  tok->size = 0;
+  tok->parent = -1;
+  return tok;
+}
+
+
+/**
+ * Fills token type and boundaries.
+ */
+static void
+jsmn_fill_token
+(jsmntok_t   *token
+,jsmntype_t   type
+,int          start
+,int          end
+)
+{
+  token->type = type;
+  token->start = start;
+  token->end = end;
+  token->size = 0;
+}
+
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int
+jsmn_parse_primitive
+(jsmn_parser *parser
+,const char  *js
+,size_t       len
+,jsmntok_t   *tokens
+,size_t       num_tokens
+)
+{
+  jsmntok_t  *token;
+  int         start;
+
+  start = parser->pos;
+
+  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) 
+  {
+    switch (js[parser->pos])
+    {
+      case '\t':
+      case '\r': 
+      case '\n': 
+      case ' ' :
+      case ',' : 
+      case ']' : 
+      case '}' :
+        goto found;
+    }
+    if (js[parser->pos] < 32 || js[parser->pos] >= 127)
+    {
+      parser->pos = start;
+      return JSMN_ERR_INVAL;
+    }
+  }
+  parser->pos = start;
+  return JSMN_ERR_PART;
+
+found:
+  if (tokens == NULL)
+  {
+    parser->pos--;
+    return 0;
+  }
+  
+  token = jsmn_alloc_token(parser, tokens, num_tokens);
+  if (token == NULL)
+  {
+    parser->pos = start;
+    return JSMN_ERR_NOMEM;
+  }
+  jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+
+  token->parent = parser->toksuper;
+  parser->pos--;
+
+  return 0;
+}
+
+
+/**
+ * Fill next token with JSON string.
+ */
+static int
+jsmn_parse_string
+(jsmn_parser *parser
+,const char  *js
+,size_t       len
+,jsmntok_t   *tokens
+,size_t       num_tokens
+)
+{
+  jsmntok_t  *token;
+  int start = parser->pos;
+
+  parser->pos++;
+
+  /* Skip starting quote */
+  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) 
+  {
+    char c = js[parser->pos];
+
+    /* Quote: end of string */
+    if (c == '\"')
+    {
+      if (tokens == NULL)
+        return 0;
+
+      token = jsmn_alloc_token(parser, tokens, num_tokens);
+      if (token == NULL)
+      {
+        parser->pos = start;
+        return JSMN_ERR_NOMEM;
+      }
+
+      jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
+      token->parent = parser->toksuper;
+      return 0;
+    }
+
+    /* Backslash: Quoted symbol expected */
+    if (c == '\\')
+    {
+      parser->pos++;
+      switch (js[parser->pos])
+      {
+        /* Allowed escaped symbols */
+        case '\"': 
+        case '/' : 
+        case '\\': 
+        case 'b' :
+        case 'f' : 
+        case 'r' : 
+        case 'n' : 
+        case 't' :
+          break;
+
+        /* Allows escaped symbol \uXXXX */
+        case 'u':
+          parser->pos++;
+          int i = 0;
+          for (; i < 4 && js[parser->pos] != '\0'; i++) 
+          {
+            /* If it isn't a hex character we have an ERR */
+            if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+                  (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+                  (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
+              parser->pos = start;
+              return JSMN_ERR_INVAL;
+            }
+            parser->pos++;
+          }
+          parser->pos--;
+          break;
+
+        /* Unexpected symbol */
+        default:
+          parser->pos = start;
+          return JSMN_ERR_INVAL;
+      }
+    }
+  }
+  parser->pos = start;
+  return JSMN_ERR_PART;
+}
+
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+int 
+jsmn_parse
+(jsmn_parser *parser
+,const char  *js
+,size_t       len
+,jsmntok_t   *tokens
+,unsigned int num_tokens
+)
+{
+  int         r;
+  int         i;
+  jsmntok_t  *token;
+  int         count = 0;
+
+  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) 
+  {
+    char      c;
+    jsmntype_t type = JSMN_INVALID;
+
+    c = js[parser->pos];
+    switch (c)
+    {
+      case '{' : 
+      case '[' :
+        count++;
+        if (tokens == NULL)
+          break;
+
+        token = jsmn_alloc_token(parser, tokens, num_tokens);
+
+        if (token == NULL)
+          return JSMN_ERR_NOMEM;
+
+        if (parser->toksuper != -1)
+        {
+          tokens[parser->toksuper].size++;
+          token->parent = parser->toksuper;
+        }
+        token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+        token->start = parser->pos;
+        parser->toksuper = parser->toknext - 1;
+        break;
+
+      case '}': 
+      case ']':
+        if (tokens == NULL)
+          break;
+
+        type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+
+        if (parser->toknext < 1)
+          return JSMN_ERR_INVAL;
+
+        token = &tokens[parser->toknext - 1];
+        for (;;)
+        {
+          if (token->start != -1 && token->end == -1) 
+          {
+            if (token->type != type) 
+              return JSMN_ERR_INVAL;
+
+            token->end = parser->pos + 1;
+            parser->toksuper = token->parent;
+            break;
+          }
+
+          if (token->parent == -1) 
+            break;
+
+          token = &tokens[token->parent];
+        }
+        break;
+
+      case '\"':
+        r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+        if (r < 0)
+          return r;
+
+        count++;
+        if (parser->toksuper != -1 && tokens != NULL)
+          tokens[parser->toksuper].size++;
+        break;
+
+      case '\t': 
+      case '\r': 
+      case '\n': 
+      case ':' : 
+      case ',' : 
+      case ' ' : 
+        break;
+      
+      case '-' : 
+      case '0' : 
+      case '1' : 
+      case '2' : 
+      case '3' : 
+      case '4' :
+      case '5' : 
+      case '6' : 
+      case '7' : 
+      case '8' : 
+      case '9' : 
+      case 't' : 
+      case 'f' : 
+      case 'n' :
+        r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+        if (r < 0)
+          return r;
+
+        count++;
+
+        if (parser->toksuper != -1 && tokens != NULL)
+          tokens[parser->toksuper].size++;
+        break;
+
+      /* Unexpected char in strict mode */
+      default:
+        return JSMN_ERR_INVAL;
+    }
+  }
+
+  for (i = parser->toknext - 1; i >= 0; i--) 
+  {
+    /* Unmatched opened object or array */
+    if (tokens[i].start != -1 && tokens[i].end == -1) 
+    {
+      return JSMN_ERR_PART;
+    }
+  }
+
+  return count;
+}
+
+/**
+ * Creates a new parser based over a given  buffer with an array of tokens 
+ * available.
+ */
+void 
+jsmn_init
+(jsmn_parser *parser)
+{
+  parser->pos = 0;
+  parser->toknext = 0;
+  parser->toksuper = -1;
+}
+
+
+