/ JSMN

Open Source Software - Lab mission 3 - jsmn code analysis

분석대상 파일 : jsmn.h, jsmn.c

분석내용

  1. 변수들 모든 변수들의 이름, 타입, 의미, 용도를 찾아서 기술할 것

    jsmn.h

    타입
    1. jsmntype_t : typedef를 이용해 정의된 토큰의 타입 이고, enum으로 유형에 따라 숫자가 할당되어있다.
    typedef enum {
    	JSMN_UNDEFINED = 0,  //정의되지 않은 타입
    	JSMN_OBJECT = 1,     //오브젝트타입
    	JSMN_ARRAY = 2,      //배열타입
    	JSMN_STRING = 3,     //스트링타입
    	JSMN_PRIMITIVE = 4   //숫자나 boolean 또는 null타입
    } jsmntype_t;
    
    1. jsmntok_t : typedef로 정의된 타입으로, JSON 토큰에 관한 타입이다.
    2. jsmn_parser : typedef로 정의된 타입으로, 이용가능한 토큰블록의 배열을 저장하는 타입이다.
    변수
    typedef struct {
    	jsmntype_t type; // 토큰의 타입
    	int start;       // 토큰의 시작 위치
    	int end;         // 토큰의 끝 위치
    	int size;        // child tokens의 수
    #ifdef JSMN_PARENT_LINKS
    	int parent;
    #endif
    } jsmntok_t;
    
    1. type : jsmntype_t 형 변수로 enum으로 json 타입(object, array, string etc.)에 따라 값이 할당된다.
    2. start : int형 변수로 JSON 데이터 스트링의 시작 위치를 나타낸다.
    3. end : int형 변수로 JSON 데이터 스트링의 끝 위치를 나타낸다.
    4. size : int형 변수로 child token의 수를 나타낸다.
    5. parent : int형 변수로, JSMN_PARENT_LINKS가 존재할 경우에만 할당된다.
    typedef struct {
    	unsigned int pos; /* offset in the JSON string */
    	unsigned int toknext; /* next token to allocate */
    	int toksuper; /* superior token node, e.g parent object or array */
    } jsmn_parser;
    
    1. pos : unsigned int 타입으로 JSON 스트링안의 offset을 의미한다.

    2. toknext : 다음에 할당될 토큰을 의미한다.

    3. toksuper : parent object나 array 같이 최상위 토큰 노드를 의미한다.

    jsmn.c

    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;
    #ifdef JSMN_PARENT_LINKS
    	tok->parent = -1;
    #endif
    	return tok;
    }
    
    1. tok : jsmntok_t 타입 (JSON 토큰에 대한 타입)으로 선언된 포인터 변수
    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]) {
    #ifndef JSMN_STRICT
    			/* In strict mode primitive must be followed by "," or "}" or "]" */
    			case ':':
    #endif
    			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_ERROR_INVAL;
    		}
    	}
    #ifdef JSMN_STRICT
    	/* In strict mode primitive must be followed by a comma/object/array */
    	parser->pos = start;
    	return JSMN_ERROR_PART;
    #endif
    
    found:
    	if (tokens == NULL) {
    		parser->pos--;
    		return 0;
    	}
    	token = jsmn_alloc_token(parser, tokens, num_tokens);
    	if (token == NULL) {
    		parser->pos = start;
    		return JSMN_ERROR_NOMEM;
    	}
    	jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
    #ifdef JSMN_PARENT_LINKS
    	token->parent = parser->toksuper;
    #endif
    	parser->pos--;
    	return 0;
    }
    
    1. start : int형 변수로 파싱할 시작위치를 나타낸다.
    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 = parser->toknext;
    
    	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
    		char c;
    		jsmntype_t type;
    
    		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_ERROR_NOMEM;
    				if (parser->toksuper != -1) {
    					tokens[parser->toksuper].size++;
              
           //중간생략 
    	return count;
    }
    
    1. count : int 형 변수로 토큰의 개수를 세는 변수이다.
  2. 모든 함수들의 이름, 파라미터의 종류와 의미, 리턴값의 의미, 함수의 수행내용을 정리하여 기술할 것.

    jsmn.h

    1. void jsmn_init(jsmn_parser *parser);
      

      jsmn_init 함수는 jsmn_parser 타입의 포인터 변수인 parser을 파라미터로 받아 new JSON parser를 만드는 기능의 함수이다. void 타입의 함수이기 때문에 리턴값은 없다. jsmn.c 에서 파서를 초기화하는 함수를 선언해준 것이다.

    2. int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
      		jsmntok_t *tokens, unsigned int num_tokens);
      

      int jsmn_parse 함수는 jsmn_parser타입의 포인트변수인 *parser와 const char 타입의 포인터 변수 js 와 size_t 변수 len, jsmntok_t 타입의 포인터변수 tokens, unsigned int 타입의 num_tokens를 파라미터로 받아서 JSON parser를 실행하는 역할을 한다. 여기서는 jsmn.c에 있는 함수를 선언하는 것이다.

    jsmn.c

    1. static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
      		jsmntok_t *tokens, size_t num_tokens)
      

      parser 파라미터를 통해 현재 할당할 토큰에 대해 전달받아, tokens 이라는 배열에 저장한다. num_tokens 값을 파라미터로 받아서 num_tokens의 값이 다음토큰의 인덱스보다 작을때 까지만 저장한다. 반환값으로는 tok가 가르키는 주소 값을 반환한다. 이 함수는 토큰 풀로부터 새로운 사용되지 않은 토큰을 할당하는 함수이다.

    2. 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;
      }
      

      파라미터를 통해 전달한 토큰에 type과 start 값, end 값을 해당 token에 채워주는 함수이다.

    3. static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
      		size_t len, jsmntok_t *tokens, size_t num_tokens)
      

      jsmn_parse함수에서 JSON을 분석하여 그 타입이 primitive 타입일 때, 파라미터와 함께 이 함수를 호출합니다. 이 함수를 호출 했을 때, 다음 사용 가능한 토큰을 JSON primitive로 채 웁니다. 파라미터로 온 해당되는 primitive 값을 해당하는 토큰 개수만큼 토큰에 저장한다.

    4. static int jsmn_parse_string(jsmn_parser *parser, const char *js,
      		size_t len, jsmntok_t *tokens, size_t num_tokens)
      

      jsmn_parse에서 JSON 데이터를 파싱할 때 만약 string 값이 존재한다면 이 함수를 호출한다. 그래서 파라미터에서 받은 parser의 주소값과 그 스트링을 파라미터에서 받은 수만큼 저장한다. JSON string을 분석해 string 값만 토큰에 채우는 함수이다.

    5. int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
      		jsmntok_t *tokens, unsigned int num_tokens) 
      

      JSON data를 분석하는 jsmn의 핵심 함수라고 할 수 있다. 먼저 {나 (를 감지하여서 object의 수를 count에다 저장하고, 해당하는 token이 null이 아니라면 jsmn_alloc_token함수를 호출하여 새로운 사용하지 않은 토큰을 할당받는다. 그리고 할당받은 토큰의 타입과 시작위치 등의 값을 할당한다. { 일 경우, object 타입으로 ,[일 경우 array타입으로 판단한다. 그리고 }, ] 를 감지함을 통해 object나 array의 끝을 판단한다. 그리고 파싱을 하는 과정 중 jsmn.h에서 규정한 에러의 유형이 발생하면 JSMN_ERROR_NOMEM, JSMN_ERROR_INVAL, JSMN_ERROR_PART 등의 에러를 리턴한다. 그리고 ‘"’,’ ‘, ‘\t’, ‘:’, 등을

      파싱하다 발견하면 string단위로 구분하여 jsmn_parse_string()를 호출하여 string 단위로 토큰에 저장한다. 그리고 숫자나 -, t ,f, n을 발견하면 jsmn_parse_primitive()를 호출하여 primitive type을 저장하는 토큰에 저장한다.

    6. void jsmn_init(jsmn_parser *parser) 
      

      parser의 주소값을 파라미터로 받아서 사용 가능한 토큰 배열을 사용하여 제공된 버퍼를 기반으로 새 파서를 만드는 함수이다. ​ ​

  3. JSON 간단한 샘플을 찾아서 어떤 용도로 쓰이는지 정리하여 기술할 것

'{ "name" : "Jack", "age" : 27 }'

위와 같은 JSON 형식으로 저장된 object가 있다고 하면 jack 이라는 이름과 27이라는 나이를 object에 포함하고 있다. jsmn은 이와 같은 JSON 데이터를 여러가지로 나누어 토큰에 저장한다. object 토큰에는 전체 object를 통째로 저장하고, 그 후에 strings이 파싱되면 object에서 “name”, “jack” ,”age” 처럼 string을 분리하여 토큰을 저장한다. 그리고 primitive type 또한 나뉘어 따로 토큰에 저장하는 도구이자 라이브러리라고 할 수 있다.

아래는 jsmn이 나누어 토큰에 저장하는 타입의 형식이다.

  • Object - a container of key-value pairs, e.g.: { "foo":"bar", "x":0.3 }
  • Array - a sequence of values, e.g.: [ 1, 2, 3 ]
  • String - a quoted sequence of chars, e.g.: "foo"
  • Primitive - a number, a boolean (true, false) or null

예를 들어 아래와 같은 형식의 JSON이 있다고 가정하면,

myObj = {
    "name":"John",
    "age":30,
    "cars": [
        { "name":"Ford", "models":[ "Fiesta", "Focus", "Mustang" ] },
        { "name":"BMW", "models":[ "320", "X3", "X5" ] },
        { "name":"Fiat", "models":[ "500", "Panda" ] }
    ]
 }

jsmn을 통해서 파싱하면 object 에는 전체 오브젝트와 []로 둘러쌓여있는 배열들이 저장되고, “ “로 구분되어있는 string별로 저장되며, 30과 같은 primitive type도 따로 분류된다. 우리는 json format을 사용해 key-value pair notation과 같은 형식으로 여러 타입을 저장할 수 있고, json파싱을 통하여 아래와 같이 저장되는 구조와 형식을 변화 시킬수도 있다.

{
  "colors": [
    {
      "color": "black",
      "category": "hue",
      "type": "primary",
      "code": {
        "rgba": [255,255,255,1],
        "hex": "#000"
      }
    },
    {
      "color": "white",
      "category": "value",
      "code": {
        "rgba": [0,0,0,1],
        "hex": "#FFF"
      }
    }
}
-----------------------------------------------------------------------------------
{
  "aliceblue": "#f0f8ff",
  "antiquewhite": "#faebd7",
  "aqua": "#00ffff",
  "aquamarine": "#7fffd4",
  "azure": "#f0ffff",
  "beige": "#f5f5dc",
  "bisque": "#ffe4c4",
  "black": "#000000",
  "blanchedalmond": "#ffebcd",
  "blue": "#0000ff",
  "blueviolet": "#8a2be2",
  "brown": "#a52a2a",
}
-----------------------------------------------------------------------------------{
  "aliceblue": [240, 248, 255, 1],
  "antiquewhite": [250, 235, 215, 1],
  "aqua": [0, 255, 255, 1],
  "aquamarine": [127, 255, 212, 1],
  "azure": [240, 255, 255, 1],
  "beige": [245, 245, 220, 1],
  "bisque": [255, 228, 196, 1],
  "black": [0, 0, 0, 1],
  "blanchedalmond": [255, 235, 205, 1],
  "blue": [0, 0, 255, 1],
  "blueviolet": [138, 43, 226, 1],
  "brown": [165, 42, 42, 1],
  "burlywood": [222, 184, 135, 1],
  "cadetblue": [95, 158, 160, 1],
  "chartreuse": [127, 255, 0, 1],
  "chocolate": [210, 105, 30, 1],
  "coral": [255, 127, 80, 1],
}

아래는 CRA 동아리에서 방학 프로젝트에서 사용했던 배달 어플리케이션의 json 포맷 파일의 내용이다.

아래와 같이 다양한 형식으로 다양한 정보를 담는 것이 가능하다.


    {
        "id": 0,
        "category" : "others",
        "name": "59쌀피자",       
        "image": "59쌀피자양덕점.jpg",
        "time": "open: 11:00 ~ close 23:00",
        "number": "Tel: 054-249-0059"
 
    },
    {
        "id": 1,
        "category" : "chicken",
        "name": "BBQ 장량점",
        "image": "bbq장량점.jpg",
        "time": "open 12:00 ~ close 23:59 ",
        "number" : "Tel: 054-252-9283"
    },
    {
        "id": 2,
        "category" : "others",
        "name" : "Dessert39 양덕점",
        "image": "dessert39양덕점.jpg",
        "time": "open 10:00 ~ close 22:00  ",
        "number" : "Tel: 054-243-7294"
    },
    {
        "id": 2,
        "category" : "chicken",
        "name" : "강호동치킨",
        "image": "강호동치킨.jpg",
        "time": "open 13:00 ~ close 01:00  ",
        "number" : "Tel: 054-256-0678"
    },
    {
        "id": 2,
        "category" : "chicken",
        "name" : "굽네치킨",
        "image": "굽네치킨.jpg",
        "time": "open 12:00 ~ close 23:59 연중무휴",
        "number" : "Tel: 054-252-9595"
    },
    {
        "id": 2,
        "category" : "chicken",
        "name" : "그 시절 옛날 통닭",
        "image": "그시절옛날통닭.jpg",
        "time": "open 15:00 ~ close 23:59 연중무휴",
        "number" : "Tel: 054-255-2534"
    },
    {
        "id": 2,
        "category" : "chicken",
        "name" : "김스타치킨",
        "image": "김스타치킨.jpg",
        "time": "open 16:00 ~ close 02:00 연중무휴",
        "number" : "Tel: 054-255-5666"
    },
    {
        "id": 2,
        "category" : "chicken",
        "name" : "꼬꼬바베큐",
        "image": "꼬꼬바베큐.jpg",
        "time": "open 15:00 ~ close 01:00",
        "number" : "Tel: 054-262-9220"
    }