int dom_null(JSAXContextRef ctxt) { DomInfo *data = getDOMContext(ctxt); // no handle to the context CHECK_CONDITION_RETURN_VALUE(data == NULL, 0, "null encountered without any context"); // no parent node CHECK_CONDITION_RETURN_VALUE(data->m_prev == NULL, 0, "unexpected state - how is this possible?"); SANITY_CHECK_POINTER(ctxt); SANITY_CHECK_POINTER(data->m_prev); if (data->m_value == NULL) { CHECK_CONDITION_RETURN_VALUE(!jis_array(data->m_prev->m_value), 0, "Improper place for null"); jarray_append(data->m_prev->m_value, jnull()); } else if (jis_string(data->m_value)) { CHECK_CONDITION_RETURN_VALUE(!jis_object(data->m_prev->m_value), 0, "Improper place for null"); jobject_put(data->m_prev->m_value, data->m_value, jnull()); data->m_value = NULL; } else { PJ_LOG_ERR("PBNJSON_NULL_VALUE_WO_KEY", 0, "value portion of key-value pair without a key"); return 0; } return 1; }
jvalue_ref jdom_parse_file(const char *file, JSchemaInfoRef schemaInfo, JFileOptimizationFlags flags) { CHECK_POINTER_RETURN_NULL(file); CHECK_POINTER_RETURN_NULL(schemaInfo); int fd; off_t fileSize; raw_buffer input = { 0 }; jvalue_ref result; char *err_msg; fd = open(file, O_RDONLY); if (fd == -1) { goto errno_parse_failure; } if (!file_size(fd, &fileSize)) { goto errno_parse_failure; } input.m_len = fileSize; if (input.m_len != fileSize) { PJ_LOG_ERR("File too big - currently unsupported by this API"); close(fd); } if (flags & JFileOptMMap) { input.m_str = (char *)mmap(NULL, input.m_len, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0); if (input.m_str == NULL || input.m_str == MAP_FAILED) { goto errno_parse_failure; } } else { input.m_str = (char *)malloc(input.m_len + 1); if (input.m_len != read(fd, (char *)input.m_str, input.m_len)) { goto errno_parse_failure; } ((char *)input.m_str)[input.m_len] = 0; } result = jdom_parse(input, DOMOPT_INPUT_OUTLIVES_WITH_NOCHANGE, schemaInfo); return_result: close(fd); if (UNLIKELY(jis_null(result))) { if (input.m_str) { if (flags & JFileOptMMap) { munmap((void *)input.m_str, input.m_len); } else { free((void *)input.m_str); } } } else { result->m_backingBuffer = input; result->m_backingBufferMMap = flags & JFileOptMMap; } return result; errno_parse_failure: err_msg = strdup(strerror(errno)); PJ_LOG_WARN("Attempt to parse json document '%s' failed (%d) : %s", file, errno, err_msg); free(err_msg); result = jnull(); goto return_result; }
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; }