static bool jsax_parse_internal(PJSAXCallbacks *parser, raw_buffer input, JSchemaInfoRef schemaInfo, void **ctxt, bool logError, bool comments) { yajl_status parseResult; PJ_LOG_TRACE("Parsing '%.*s'", RB_PRINTF(input)); if (parser == NULL) parser = &no_callbacks; if (jis_null_schema(schemaInfo->m_schema)) { PJ_LOG_WARN("Cannot match against schema that matches nothing: Schema pointer = %p", schemaInfo->m_schema); return false; } if (schemaInfo->m_schema == jschema_all()) { PJ_LOG_DBG("Using default empty schema for matching"); } else { if (schemaInfo->m_resolver == NULL) { PJ_LOG_DBG("No resolver specified for the schema. Make sure %p doesn't contain any external references", schemaInfo->m_schema); } } if (schemaInfo->m_errHandler == NULL) schemaInfo->m_errHandler = &null_err_handler; #ifdef _DEBUG logError = true; #endif yajl_callbacks yajl_cb = { (pj_yajl_null)parser->m_null, // yajl_null (pj_yajl_boolean)parser->m_boolean, // yajl_boolean NULL, // yajl_integer NULL, // yajl_double (pj_yajl_number)parser->m_number, // yajl_number (pj_yajl_string)parser->m_string, // yajl_stirng (pj_yajl_start_map)parser->m_objStart, // yajl_start_map (pj_yajl_map_key)parser->m_objKey, // yajl_map_key (pj_yajl_end_map)parser->m_objEnd, // yajl_end_map (pj_yajl_start_array)parser->m_arrStart, // yajl_start_array (pj_yajl_end_array)parser->m_arrEnd, // yajl_end_array }; yajl_parser_config yajl_opts = { comments, // comments are not allowed 0, // currently only UTF-8 will be supported for input. }; PJSAXContext internalCtxt = { .ctxt = (ctxt != NULL ? *ctxt : NULL), .m_handlers = &yajl_cb, .m_errors = schemaInfo->m_errHandler, }; #if !BYPASS_SCHEMA internalCtxt.m_validation = jschema_init(schemaInfo); if (internalCtxt.m_validation == NULL) { PJ_LOG_WARN("Failed to initialize validation state machine"); return false; } #endif yajl_handle handle = yajl_alloc(&my_bounce, &yajl_opts, NULL, &internalCtxt); parseResult = yajl_parse(handle, (unsigned char *)input.m_str, input.m_len); if (ctxt != NULL) *ctxt = jsax_getContext(&internalCtxt); switch (parseResult) { case yajl_status_ok: break; case yajl_status_client_canceled: if (ERR_HANDLER_FAILED(schemaInfo->m_errHandler, m_unknown, &internalCtxt)) goto parse_failure; PJ_LOG_WARN("Client claims they handled an unknown error in '%.*s'", (int)input.m_len, input.m_str); break; case yajl_status_insufficient_data: if (ERR_HANDLER_FAILED(schemaInfo->m_errHandler, m_parser, &internalCtxt)) goto parse_failure; PJ_LOG_WARN("Client claims they handled incomplete JSON input provided '%.*s'", (int)input.m_len, input.m_str); break; case yajl_status_error: default: if (ERR_HANDLER_FAILED(schemaInfo->m_errHandler, m_unknown, &internalCtxt)) goto parse_failure; PJ_LOG_WARN("Client claims they handled an unknown error in '%.*s'", (int)input.m_len, input.m_str); break; } #if !BYPASS_SCHEMA jschema_state_release(&internalCtxt.m_validation); #endif #ifndef NDEBUG assert(yajl_get_error(handle, 0, NULL, 0) == NULL); #endif yajl_free(handle); return true; parse_failure: if (UNLIKELY(logError)) { unsigned char *errMsg = yajl_get_error(handle, 1, (unsigned char *)input.m_str, input.m_len); PJ_LOG_WARN("Parser reason for failure:\n'%s'", errMsg); yajl_free_error(handle, errMsg); } #if !BYPASS_SCHEMA jschema_state_release(&internalCtxt.m_validation); #endif yajl_free(handle); return false; } bool jsax_parse_ex(PJSAXCallbacks *parser, raw_buffer input, JSchemaInfoRef schemaInfo, void **ctxt, bool logError) { return jsax_parse_internal(parser, input, schemaInfo, ctxt, logError, false); } bool jsax_parse(PJSAXCallbacks *parser, raw_buffer input, JSchemaInfoRef schema) { assert(schema != NULL); return jsax_parse_ex(parser, input, schema, NULL, false); }
bool jsax_parse_ex(PJSAXCallbacks *parser, raw_buffer input, JSchemaInfoRef schemaInfo, void **ctxt) { return jsax_parse_internal(parser, input, schemaInfo, ctxt); }
jvalue_ref jdom_parse_ex(raw_buffer input, JDOMOptimizationFlags optimizationMode, JSchemaInfoRef schemaInfo, bool allowComments) { jvalue_ref result; PJSAXCallbacks callbacks = { dom_object_start, // m_objStart dom_object_key, // m_objKey dom_object_end, // m_objEnd dom_array_start, // m_arrStart dom_array_end, // m_arrEnd dom_string, // m_string dom_number, // m_number dom_boolean, // m_boolean dom_null, // m_null }; DomInfo *topLevelContext = calloc(1, sizeof(struct DomInfo)); CHECK_POINTER_RETURN_NULL(topLevelContext); void *domCtxt = topLevelContext; bool parsedOK = jsax_parse_internal(&callbacks, input, schemaInfo, &domCtxt, false /* don't log errors*/, allowComments); result = topLevelContext->m_value; if (domCtxt != topLevelContext) { // unbalanced state machine (probably a result of parser failure) // cleanup so there's no memory leak PJ_LOG_ERR("state machine indicates invalid input"); parsedOK = false; DomInfo *ctxt = domCtxt; DomInfo *parentCtxt; while (ctxt) { #ifdef _DEBUG if (ctxt == topLevelContext) { assert(ctxt->m_prev == NULL); } else { assert(ctxt->m_prev != NULL); } #endif parentCtxt = ctxt->m_prev; // top-level json value can only be an object or array, // thus we do not need to check that we aren't releasing topLevelContext->m_value. // the only other object type that m_value will contain is string (representing the key of an object). //if (ctxt->m_value && !jis_array(ctxt->m_value) && !jis_object(ctxt->m_value)) { if (ctxt->m_value && jis_string(ctxt->m_value)) { j_release(&ctxt->m_value); } free(ctxt); ctxt = parentCtxt; } topLevelContext = NULL; } free(topLevelContext); if (!parsedOK) { PJ_LOG_ERR("Parser failure"); j_release(&result); return jnull(); } if (result == NULL) PJ_LOG_ERR("result was NULL - unexpected. input was '%.*s'", (int)input.m_len, input.m_str); else if (result == jnull()) PJ_LOG_WARN("result was NULL JSON - unexpected. input was '%.*s'", (int)input.m_len, input.m_str); else { if ((optimizationMode & (DOMOPT_INPUT_NOCHANGE | DOMOPT_INPUT_OUTLIVES_DOM | DOMOPT_INPUT_NULL_TERMINATED)) && input.m_str[input.m_len] == '\0') { result->m_toString = (char *)input.m_str; result->m_toStringDealloc = NULL; } } return result; }