/* Write the contents of *src as JSON, and append the JSON string to *dst. * This will use strlen() to determine the start offset, and ta_get_size() * and ta_realloc() to extend the memory allocation of *dst. * Returns: 0 on success, <0 on failure. */ int json_write(char **dst, struct mpv_node *src) { bstr buffer = bstr0(*dst); int r = json_append(&buffer, src); *dst = buffer.start; return r; }
static int json_append_str(char **dst, struct mpv_node *src, int indent) { bstr buffer = bstr0(*dst); int r = json_append(&buffer, src, indent); *dst = buffer.start; return r; }
static int json_append(bstr *b, const struct mpv_node *src, int indent) { switch (src->format) { case MPV_FORMAT_NONE: APPEND(b, "null"); return 0; case MPV_FORMAT_FLAG: APPEND(b, src->u.flag ? "true" : "false"); return 0; case MPV_FORMAT_INT64: bstr_xappend_asprintf(NULL, b, "%"PRId64, src->u.int64); return 0; case MPV_FORMAT_DOUBLE: bstr_xappend_asprintf(NULL, b, "%f", src->u.double_); return 0; case MPV_FORMAT_STRING: write_json_str(b, src->u.string); return 0; case MPV_FORMAT_NODE_ARRAY: case MPV_FORMAT_NODE_MAP: { struct mpv_node_list *list = src->u.list; bool is_obj = src->format == MPV_FORMAT_NODE_MAP; APPEND(b, is_obj ? "{" : "["); int next_indent = indent >= 0 ? indent + 1 : -1; for (int n = 0; n < list->num; n++) { if (n) APPEND(b, ","); add_indent(b, next_indent); if (is_obj) { write_json_str(b, list->keys[n]); APPEND(b, ":"); } json_append(b, &list->values[n], next_indent); } add_indent(b, indent); APPEND(b, is_obj ? "}" : "]"); return 0; } } return -1; // unknown format }
json_value *json_parse(char *source, char **error_pos, int *error_line, block_allocator *allocator) { json_value *root = 0; json_value *top = 0; char *name = 0; char *it = source; int escaped_newlines = 0; while (*it) { switch (*it) { case '{': case '[': { // create new value json_value *object = json_alloc(allocator); // name object->name = name; name = 0; // type object->type = (*it == '{') ? JSON_OBJECT : JSON_ARRAY; // skip open character ++it; // set top and root if (top) { json_append(top, object); } else if (!root) { root = object; } else { ERROR(it, "Second root. Only one root allowed"); } top = object; } break; case '}': case ']': { if (!top || top->type != ((*it == '}') ? JSON_OBJECT : JSON_ARRAY)) { ERROR(it, "Mismatch closing brace/bracket"); } // skip close character ++it; // set top top = top->parent; } break; case ':': if (!top || top->type != JSON_OBJECT) { ERROR(it, "Unexpected character"); } ++it; break; case ',': CHECK_TOP(); ++it; break; case '"': { CHECK_TOP(); // skip '"' character ++it; char *first = it; char *last = it; while (*it) { if ((unsigned char)*it < '\x20') { ERROR(first, "Control characters not allowed in strings"); } else if (*it == '\\') { switch (it[1]) { case '"': *last = '"'; break; case '\\': *last = '\\'; break; case '/': *last = '/'; break; case 'b': *last = '\b'; break; case 'f': *last = '\f'; break; case 'n': *last = '\n'; ++escaped_newlines; break; case 'r': *last = '\r'; break; case 't': *last = '\t'; break; case 'u': { unsigned int codepoint; if (hatoui(it + 2, it + 6, &codepoint) != it + 6) { ERROR(it, "Bad unicode codepoint"); } if (codepoint <= 0x7F) { *last = (char)codepoint; } else if (codepoint <= 0x7FF) { *last++ = (char)(0xC0 | (codepoint >> 6)); *last = (char)(0x80 | (codepoint & 0x3F)); } else if (codepoint <= 0xFFFF) { *last++ = (char)(0xE0 | (codepoint >> 12)); *last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F)); *last = (char)(0x80 | (codepoint & 0x3F)); } } it += 4; break; default: ERROR(first, "Unrecognized escape sequence"); } ++last; it += 2; } else if (*it == '"') { *last = 0; ++it; break; } else { *last++ = *it++; } }
int json_parse_object( json_task_t *task, json_object_t *parent ) { char ch; json_object_t node, * append = NULL; node.next = parent; node.key = NULL; node.key_len = 0; if( !task->callback ) { if( !task->root ) { append = task->root = json_create_object(); } else { append = parent->value.p = json_create_object(); } } task->status = STS_OBJECT_START; while(( ch = *(task->str + task->count) )) { task->count ++; if( ch == ' ' || ch == '\n' || ch == '\t' ) { continue; } switch( task->status ) { case STS_OBJECT_START: if( ch == '"' ) { node.key = task->str + task->count; if( json_parse_string( task ) != 0 ) { return -1; } // WARNING: key_len 可能发生溢出 node.key_len = task->str + task->count - node.key - 1; task->status = STS_OBJECT_COLON; } else if( ch == '}' ) { // 空对象 {},忽略 return 0; } else { task->err_msg = "expect '\"' or '}'"; return -1; } break; case STS_OBJECT_COLON: if( ch == ':' ) { task->status = STS_OBJECT_VALUE; } else { task->err_msg = "expect ':'"; return -1; } break; case STS_OBJECT_VALUE: /* 这里需要将计数减一,因为 json_parse_value 需要根据这一个字符判断 value 类型 */ task->count --; node.value.s = task->str + task->count; if( json_parse_value( task, &node ) != 0 ) { if( node.value_type == JSON_OBJECT || node.value_type == JSON_ARRAY ) { json_delete_object(node.value.p); } return -1; } // WARNING: value_len 可能发生溢出 node.value_len = task->str + task->count - node.value.s; task->status = STS_OBJECT_COMMA; break; case STS_OBJECT_COMMA: if( ( ch == ',' || ch == '}' ) ) { // 对 value 进行处理 switch(node.value_type) { case JSON_STRING: // 去除字符串两端的引号 node.value.s += 1; node.value_len -= 2; break; case JSON_DOUBLE: node.value.d = atof(node.value.s); break; case JSON_LONGLONG: node.value.l = atoll(node.value.s); break; case JSON_ARRAY: case JSON_OBJECT: break; } if( task->callback ) { task->callback( task, &node ); } if( !task->callback ) { switch( node.value_type ) { case JSON_ARRAY: case JSON_OBJECT: case JSON_STRING: append = json_append(append, node.key, node.key_len, node.value_type, node.value.p, node.value_len); break; default: append = json_append(append, node.key, node.key_len, node.value_type, &node.value, node.value_len); } } } if( ch == ',' ) { task->status = STS_OBJECT_START; } else if( ch == '}' ) { return 0; } else { task->err_msg = "expect ',' or '}'"; return -1; } break; default: task->err_msg = "unknown status"; return -1; } } task->err_msg = "unexpect EOF"; return -1; }
int json_parse_array( json_task_t *task, json_object_t *parent ) { char ch; json_object_t node, * append = NULL; node.next = parent; node.key = NULL; node.key_len = 0; if( !task->callback ) { if( !task->root ) { append = task->root = json_create_array(); } else { append = parent->value.p = json_create_array(); } } task->status = STS_ARRAY_START; while(( ch = *(task->str + task->count) )) { task->count ++; if( ch == ' ' || ch == '\n' || ch == '\t' ) { continue; } switch( task->status ) { case STS_ARRAY_START: if( ch == ']' ) { return 0; } else { /* 这里需要将计数减一,因为 json_parse_value 需要根据这一个字符判断 value 类型 */ task->count --; node.value.s = task->str + task->count; if( json_parse_value( task, &node ) != 0 ) { if( node.value_type == JSON_OBJECT || node.value_type == JSON_ARRAY ) { json_delete_object(node.value.p); } return -1; } // WARNING: value_len 可能发生溢出 node.value_len = task->str + task->count - node.value.s; // 对 value 进行处理 switch(node.value_type) { case JSON_STRING: // 去除字符串两端的引号 node.value.s += 1; node.value_len -= 2; break; case JSON_DOUBLE: // TODO 在解析测试用例时,atof 与 atoll 使解析时间延长了几乎一半 // 移除对数值的默认解析可以显著提升性能 node.value.d = atof(node.value.s); break; case JSON_LONGLONG: node.value.l = atoll(node.value.s); break; case JSON_ARRAY: case JSON_OBJECT: break; } // 需要放在 node.key_len ++ 之前以便正确输出数组下标 if( task->callback ) { task->callback( task, &node ); } if( !task->callback ) { switch( node.value_type ) { case JSON_ARRAY: case JSON_OBJECT: case JSON_STRING: append = json_append(append, node.key, node.key_len, node.value_type, node.value.p, node.value_len); break; default: append = json_append(append, node.key, node.key_len, node.value_type, &node.value, node.value_len); } } node.key_len ++; task->status = STS_ARRAY_COMMA; } break; case STS_ARRAY_COMMA: if( ch == ',' ) { task->status = STS_ARRAY_START; } else if( ch == ']' ) { return 0; } else { task->err_msg = "expect ',' or ']'"; return -1; } break; default: task->err_msg = "unknown status"; return -1; } } task->err_msg = "unexpect EOF"; return -1; }