int dom_string(JSAXContextRef ctxt, const char *string, size_t stringLen) { DomInfo *data = getDOMContext(ctxt); CHECK_CONDITION_RETURN_VALUE(data == NULL, 0, "string encountered without any context"); CHECK_CONDITION_RETURN_VALUE(data->m_prev == NULL, 0, "unexpected state - how is this possible?"); jvalue_ref jstr = createOptimalString(data->m_optInformation, string, stringLen); if (data->m_value == NULL) { if (UNLIKELY(!jis_array(data->m_prev->m_value))) { PJ_LOG_ERR("PBNJSON_ARR_MISPLACED_STR", 1, PMLOGKS("STRING", string), "Improper place for string"); j_release(&jstr); return 0; } jarray_append(data->m_prev->m_value, jstr); } else if (jis_string(data->m_value)) { if (UNLIKELY(!jis_object(data->m_prev->m_value))) { PJ_LOG_ERR("PBNJSON_OBJ_MISPLACED_STR", 1, PMLOGKS("STRING", string), "Improper place for string"); j_release(&jstr); return 0; } jobject_put(data->m_prev->m_value, data->m_value, jstr); data->m_value = NULL; } else { PJ_LOG_ERR("PBNJSON_STR_VALUE_WO_KEY", 1, PMLOGKS("STRING", string), "value portion of key-value pair without a key"); return 0; } return 1; }
int dom_number(JSAXContextRef ctxt, const char *number, size_t numberLen) { DomInfo *data = getDOMContext(ctxt); jvalue_ref jnum; CHECK_CONDITION_RETURN_VALUE(data == NULL, 0, "number encountered without any context"); CHECK_CONDITION_RETURN_VALUE(data->m_prev == NULL, 0, "unexpected state - how is this possible?"); CHECK_POINTER_RETURN_VALUE(number, 0); CHECK_CONDITION_RETURN_VALUE(numberLen == 0, 0, "unexpected - numeric string doesn't actually contain a number"); jnum = createOptimalNumber(data->m_optInformation, number, numberLen); if (data->m_value == NULL) { if (UNLIKELY(!jis_array(data->m_prev->m_value))) { PJ_LOG_ERR("PBNJSON_ARR_MISPLACED_NUM", 1, PMLOGKS("NUM", number), "Improper place for number"); j_release(&jnum); return 0; } jarray_append(data->m_prev->m_value, jnum); } else if (jis_string(data->m_value)) { if (UNLIKELY(!jis_object(data->m_prev->m_value))) { PJ_LOG_ERR("PBNJSON_OBJ_MISPLACED_NUM", 1, PMLOGKS("NUM", number), "Improper place for number"); j_release(&jnum); return 0; } jobject_put(data->m_prev->m_value, data->m_value, jnum); data->m_value = NULL; } else { PJ_LOG_ERR("PBNJSON_NUM_VALUE_WO_KEY", 1, PMLOGKS("NUM", number), "value portion of key-value pair without a key"); return 0; } return 1; }
static bool jobject_to_string_append (jvalue_ref jref, JStreamRef generating) { SANITY_CHECK_POINTER(jref); if (UNLIKELY(!generating->o_begin (generating))) { PJ_LOG_ERR("Schema validation error, objects are not allowed."); return false; } if (!jis_object (jref)) { const char *asStr = jvalue_tostring_internal_layer2 (jref, NULL, false); generating->string (generating, J_CSTR_TO_BUF("Internal error - not an object")); generating->string (generating, j_cstr_to_buffer(asStr)); // create invalid JSON on purpose return false; } jobject_iter it; jobject_iter_init(&it, jref); jobject_key_value key_value; while (jobject_iter_next(&it, &key_value)) { assert(jis_string(key_value.key)); if(UNLIKELY(!key_value_to_string_append(key_value, generating))) { return false; } } if (UNLIKELY(!generating->o_end (generating))) { PJ_LOG_ERR("Schema validation error, object did not validate against schema"); return false; } return true; }
static bool jarray_to_string_append (jvalue_ref jref, JStreamRef generating) { ssize_t i; assert(jis_array(jref)); if (UNLIKELY(!generating)) { PJ_LOG_ERR("Cannot append string value to NULL output stream"); return false; } SANITY_CHECK_POINTER(jref); if (UNLIKELY(jis_null(jref))) { PJ_LOG_ERR("INTERNAL ERROR!!!!!!!!!! - used internal API for array --> string for JSON null"); generating->null_value (generating); return false; } if (UNLIKELY(!generating->a_begin (generating))) { PJ_LOG_ERR("Schema validation error, arrays are not allowed"); return false; } for (i = 0; i < jarray_size (jref); i++) { jvalue_ref toAppend = jarray_get (jref, i); SANITY_CHECK_POINTER(toAppend); if (UNLIKELY(!jvalue_to_string_append (toAppend, generating))) { return false; } } if (UNLIKELY(!generating->a_end (generating))) { PJ_LOG_ERR("Schema validation error, array did not validate against schema"); return false; } return true; }
static int __number(JSAXContextRef ctxt, const char *number, size_t len) { JParser *p = static_cast<JParser *>(jsax_getContext(ctxt)); switch (SaxBounce::conversionToUse(p)) { case JParser::JNUM_CONV_RAW: return SaxBounce::n(p, std::string(number, len)); case JParser::JNUM_CONV_NATIVE: { jvalue_ref toConv = jnumber_create_unsafe(j_str_to_buffer(number, len), NULL); int64_t asInteger; double asFloat; ConversionResultFlags toFloatErrors; if (CONV_OK == jnumber_get_i64(toConv, &asInteger)) { j_release(&toConv); return SaxBounce::n(p, (asInteger)); } toFloatErrors = jnumber_get_f64(toConv, &asFloat); j_release(&toConv); return SaxBounce::n(p, asFloat, toFloatErrors); } default: PJ_LOG_ERR("PBNJSON_NO_NUMS_TYPE", 0, "Actual parser hasn't told us a valid type for how it wants numbers presented to it"); return 0; } }
static int dom_object_start(JSAXContextRef ctxt) { DomInfo *data = getDOMContext(ctxt); jvalue_ref newParent; DomInfo *newChild; CHECK_CONDITION_RETURN_VALUE(data == NULL, 0, "object encountered without any context"); newParent = jobject_create(); newChild = calloc(1, sizeof(DomInfo)); if (UNLIKELY(newChild == NULL || jis_null(newParent))) { PJ_LOG_ERR("Failed to allocate space for new object"); j_release(&newParent); free(newChild); return 0; } newChild->m_prev = data; newChild->m_optInformation = data->m_optInformation; changeDOMContext(ctxt, newChild); if (data->m_prev != NULL) { if (jis_array(data->m_prev->m_value)) { assert(data->m_value == NULL); jarray_append(data->m_prev->m_value, newParent); } else { assert(jis_object(data->m_prev->m_value)); CHECK_CONDITION_RETURN_VALUE(!jis_string(data->m_value), 0, "improper place for a child object"); jobject_put(data->m_prev->m_value, data->m_value, newParent); } } // not using reference counting here on purpose data->m_value = newParent; return 1; }
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; }
static inline bool jboolean_to_string_append (jvalue_ref jref, JStreamRef generating) { bool result = (generating->boolean (generating, jboolean_deref(jref)->value) != NULL); if (UNLIKELY(!result)) { PJ_LOG_ERR("Schema validation error, bool did not validate against schema"); } return result; }
static inline bool jstring_to_string_append (jvalue_ref jref, JStreamRef generating) { bool result = (generating->string (generating, jstring_deref(jref)->m_data) != NULL); if (UNLIKELY(!result)) { PJ_LOG_ERR("Schema validation error, string '%s' did not validate against schema", jstring_deref(jref)->m_data.m_str); } return result; }
ConversionResultFlags jdouble_to_i32(double value, int32_t *result) { CHECK_POINTER_RETURN_VALUE(result, CONV_BAD_ARGS); if (isnan(value) != 0) { PJ_LOG_WARN("PBNJSON_NAN_TO_INT_WARN", 0, "attempting to convert nan to int"); *result = 0; return CONV_NOT_A_NUM; } switch (isinf(value)) { case 0: break; case 1: PJ_LOG_WARN("PBNJSON_+INF_TO_INT_WARN", 0, "attempting to convert +infinity to int"); *result = PJSON_MAX_INT; return CONV_POSITIVE_INFINITY; case -1: PJ_LOG_WARN("PBNJSON_-INF_TO_INT_WARN", 0, "attempting to convert -infinity to int"); *result = PJSON_MIN_INT; return CONV_NEGATIVE_INFINITY; default: PJ_LOG_ERR("PBNJSON_ISINF_ERR", 1, PMLOGKFV("VALUE", "%lf", value), "unknown result from isinf for %lf", value); return CONV_GENERIC_ERROR; } if (value > PJSON_MAX_INT) { PJ_LOG_WARN("PBNJSON_DBL_OO_INT_RANGE", 1, PMLOGKFV("VALUE", "%lf", value), "attempting to convert double %lf outside of int range", value); *result = PJSON_MAX_INT; return CONV_POSITIVE_OVERFLOW; } if (value < PJSON_MIN_INT) { PJ_LOG_WARN("PBNJSON_DBL_OO_INT_RANGE", 1, PMLOGKFV("VALUE", "%lf", value), "attempting to convert double %lf outside of int range", value); *result = PJSON_MIN_INT; return CONV_NEGATIVE_OVERFLOW; } #if 0 // unnecessary for 32-bits because they will always fit in a double // with no precision loss if (value > PJSON_MAX_INT_IN_DBL || value < PJSON_MIN_INT_IN_DBL) { PJ_LOG_WARN("PBNJSON_DBL_TO_INT_CONV_WARN", 1, PMLOGKFV("VALUE", "%lf", value), "conversion of double %lf to integer potentially has precision loss", value); *result = (int64_t)value; return CONV_PRECISION_LOSS; } #endif *result = (int32_t) value; if (*result != value) { PJ_LOG_WARN("PBNJSON_DBL_TO_INT_CONV_LOSS", 1, PMLOGKFV("VALUE", "%lf", value), "conversion of double %lf results in integer with different value", value); return CONV_PRECISION_LOSS; } return CONV_OK; }
static bool jvalue_to_string_append (jvalue_ref jref, JStreamRef generating) { SANITY_CHECK_POINTER(jref); if (jref == NULL) { PJ_LOG_ERR("Internal error. Using NULL pointer instead of reference to NULL JSON object"); jref = &JNULL; } CHECK_POINTER_MSG_RETURN_VALUE(generating, false, "Internal problem due to buffer to append to being null"); bool success = false; switch (jref->m_type) { case JV_NULL: success = (generating->null_value (generating) != NULL); if (UNLIKELY(!success)) { PJ_LOG_ERR("Schema validation error, null value not accepted"); } break; case JV_OBJECT: success = jobject_to_string_append (jref, generating); break; case JV_ARRAY: success = jarray_to_string_append (jref, generating); break; case JV_NUM: success = jnumber_to_string_append (jref, generating); break; case JV_STR: success = jstring_to_string_append (jref, generating); break; case JV_BOOL: success = jboolean_to_string_append (jref, generating); break; default: PJ_LOG_ERR("Internal error. Unknown jvalue type"); break; } return success; }
int dom_object_start(JSAXContextRef ctxt) { DomInfo *data = getDOMContext(ctxt); jvalue_ref newParent; DomInfo *newChild; CHECK_CONDITION_RETURN_VALUE(data == NULL, 0, "object encountered without any context"); newParent = jobject_create(); newChild = calloc(1, sizeof(DomInfo)); if (UNLIKELY(newChild == NULL || !jis_valid(newParent))) { PJ_LOG_ERR("PBNJSON_OBJ_CALLOC_ERR", 0, "Failed to allocate space for new object"); j_release(&newParent); free(newChild); return 0; } newChild->m_prev = data; newChild->m_optInformation = data->m_optInformation; changeDOMContext(ctxt, newChild); if (data->m_prev != NULL) { if (jis_array(data->m_prev->m_value)) { assert(data->m_value == NULL); jarray_append(data->m_prev->m_value, jvalue_copy(newParent)); } else { assert(jis_object(data->m_prev->m_value)); if (UNLIKELY(!jis_string(data->m_value))) { PJ_LOG_ERR("PBNJSON_OBJ_MISPLACED_CHILD", 0, "improper place for a child object"); j_release(&newParent); return 0; } jobject_put(data->m_prev->m_value, data->m_value, jvalue_copy(newParent)); } } data->m_value = newParent; return 1; }
static const char *jvalue_tostring_internal_layer1 (jvalue_ref val, JSchemaInfoRef schemainfo, bool schemaNecessary) { if (val->m_toStringDealloc) val->m_toStringDealloc(val->m_toString); val->m_toString = NULL; const char* result = jvalue_tostring_internal_layer2 (val, schemainfo, schemaNecessary); if (result == NULL) { PJ_LOG_ERR("Failed to generate string from jvalue. Error location: %s", val->m_toString); } return result; }
int dom_boolean(JSAXContextRef ctxt, bool value) { DomInfo *data = getDOMContext(ctxt); CHECK_CONDITION_RETURN_VALUE(data == NULL, 0, "boolean encountered without any context"); CHECK_CONDITION_RETURN_VALUE(data->m_prev == NULL, 0, "unexpected state - how is this possible?"); if (data->m_value == NULL) { CHECK_CONDITION_RETURN_VALUE(!jis_array(data->m_prev->m_value), 0, "Improper place for boolean"); jarray_append(data->m_prev->m_value, jboolean_create(value)); } else if (jis_string(data->m_value)) { CHECK_CONDITION_RETURN_VALUE(!jis_object(data->m_prev->m_value), 0, "Improper place for boolean"); jobject_put(data->m_prev->m_value, data->m_value, jboolean_create(value)); data->m_value = NULL; } else { PJ_LOG_ERR("PBNJSON_BOOL_VALUE_WO_KEY", 0, "value portion of key-value pair without a key"); return 0; } return 1; }
//Helper function for jobject_to_string_append() static bool key_value_to_string_append (jobject_key_value key_value, JStreamRef generating) { //jvalue_to_string_append is enough for the key if schema validation isn't needed. //->o_key is called for validation. //jvalue_to_string_append (key_value.key, generating); if(generating->o_key(generating, jstring_deref(key_value.key)->m_data)) { //Key was OK - now process the value. if(UNLIKELY(!jvalue_to_string_append (key_value.value, generating))) { return false; } } else { PJ_LOG_ERR("Schema validation error with key: '%s'", jstring_get_fast(key_value.key).m_str); return false; } return true; }
ConversionResultFlags jstr_to_double(raw_buffer *str, double *result) { ConversionResultFlags conv_result; int64_t wholeComponent = 0; int64_t fraction = 0; int64_t fractionLeadingZeros = 0; int64_t exponent = 0; CHECK_POINTER_RETURN_VALUE(result, CONV_BAD_ARGS); conv_result = parseJSONNumber(str, &wholeComponent, &exponent, &fraction, &fractionLeadingZeros); if (UNLIKELY(CONV_IS_BAD_ARGS(conv_result) || CONV_IS_GENERIC_ERROR(conv_result))) { PJ_LOG_ERR("PBNJSON_STR_TO_NUM_ERR", 1, PMLOGKS("STRING", str->m_str), "Some weird problem converting %.*s to a number: %x", (int)str->m_len, str->m_str, conv_result); assert(false); *result = DBL_QUIET_NAN; } else if (UNLIKELY(CONV_HAS_POSITIVE_INFINITY(conv_result))) { *result = HUGE_VAL; } else if (UNLIKELY(CONV_HAS_NEGATIVE_INFINITY(conv_result))) { *result = -HUGE_VAL; } else { if (CONV_HAS_OVERFLOW(conv_result)) { // overflow that isn't infinity is precision loss assert (CONV_HAS_PRECISION_LOSS(conv_result)); } double calculatedWhole = expBase10(wholeComponent, exponent); double calculatedFraction = copysign(expBase10(fraction, exponent - fractionLeadingZeros), calculatedWhole); *result = calculatedWhole + calculatedFraction; if (isinf(*result)) conv_result |= CONV_POSITIVE_OVERFLOW; else if (-isinf(*result)) conv_result |= CONV_NEGATIVE_OVERFLOW; else if (*result == 0 && fraction != 0) conv_result |= CONV_PRECISION_LOSS; } return conv_result; }
ConversionResultFlags parseJSONNumber(raw_buffer *str, int64_t *integerPortion, int64_t *exponentPortion, int64_t *decimalPortion, int64_t *decimalLeadingZeros) { size_t i = 0; int integerMultiplier = 1; int exponentMultiplier = 1; int64_t exponent = 0; int64_t fraction = 0; int64_t fractionFactor = 0; int64_t trailingZeros = 0; bool validDecimal; int64_t temp; ConversionResultFlags result = CONV_OK; *integerPortion = 0; if (str->m_len == 0) { result = CONV_NOT_A_NUM; goto fast_stop; } switch (str->m_str[i]) { case '-': integerMultiplier = -1; i++; goto parse_integer_portion; case '0': i++; if (i >= str->m_len) goto fast_stop; switch(str->m_str[i]) { case '.': goto parse_decimal_portion; case 'e': case 'E': goto parse_exponent_portion; default: result = CONV_NOT_A_NUM; goto fast_stop; } case '1'...'9': goto parse_integer_portion; default: goto not_a_number; } parse_integer_portion: assert(integerMultiplier == 1 || integerMultiplier == -1); assert(exponent == 0); for (; i < str->m_len; i++) { switch (str->m_str[i]) { case 'e': case 'E': goto parse_exponent_portion; case '.': goto parse_decimal_portion; case '0'...'9': if (exponent == 0) { if (integerMultiplier == 1) { if (*integerPortion > (INT64_MAX / 10)) exponent = 1; } else { if (*integerPortion < (INT64_MIN) / 10) exponent = 1; } if (exponent == 0) { temp = *integerPortion * 10 + integerMultiplier * (str->m_str[i] - '0'); if (UNLIKELY(temp * integerMultiplier < 0)) { // sign flipped - overflow exponent = 1; } else { *integerPortion = temp; } } } else { if (exponent++ == INT64_MAX) return CONV_PRECISION_LOSS | (integerMultiplier == 1 ? CONV_POSITIVE_INFINITY : CONV_NEGATIVE_INFINITY); } break; default: PJ_LOG_WARN("PBNJSON_PNUM_UNXPCTD_CHAR1", 1, PMLOGKFV("CHAR", "%c", str->m_str[i]), "Unexpected character %d('%c') in '%.*s' at %zu", (int)str->m_str[i], str->m_str[i], (int)str->m_len, str->m_str, i); goto not_a_number; } } goto finish_parse; parse_decimal_portion: validDecimal = false; assert(fraction == 0); assert(fractionFactor == 0); assert(trailingZeros == 0); if (str->m_str[i] != '.') { assert(false); PJ_LOG_WARN("PBNJSON_PNUM_UNXPCTD_CHAR2", 1, PMLOGKFV("CHAR", "%c", str->m_str[i]), "Unexpected character %d('%c') in '%.*s' at %zu", (int)str->m_str[i], str->m_str[i], (int) str->m_len, str->m_str, i); goto not_a_number; } i++; for (; i < str->m_len; i++) { switch(str->m_str[i]) { case 'e': case 'E': goto parse_exponent_portion; case '0'...'9': validDecimal = true; if (str->m_str[i] == '0') { // short-circuit - trailing 0s are ignored if that's what they are. trailingZeros ++; break; } if (UNLIKELY(fractionFactor == INT64_MAX)) { assert(false); // this will only become an issue if 10^INT64_MAX < (2^((sizeof(fraction)*8) - 1) - 1) // which will never happen PJ_LOG_ERR("PBNJSON_FRACTION_ERR", 1, PMLOGKS("STRING", str->m_str), "Internal error for input: %.*s", (int)str->m_len, str->m_str); return CONV_GENERIC_ERROR; } while (trailingZeros != 0) { temp = fraction * 10; if (temp < 0) goto skip_remaining_decimal; trailingZeros--; fractionFactor++; fraction = temp; } fractionFactor++; if (fraction != INT64_MAX) { temp = fraction * 10 + (str->m_str[i] - '0'); if (UNLIKELY(temp < 0)) { fractionFactor--; goto skip_remaining_decimal; } else { fraction = temp; } } break; default: PJ_LOG_WARN("PBNJSON_PNUM_UNXPCTD_CHAR3", 1, PMLOGKFV("CHAR", "%c", str->m_str[i]), "Unexpected character %d('%c') in '%.*s' at %zu", (int)str->m_str[i], str->m_str[i], (int)str->m_len, str->m_str, i); goto not_a_number; } } if (UNLIKELY(!validDecimal)) { PJ_LOG_WARN("PBNJSON_UNXPCTD_EOS", 0, "Unexpected end of string at %zu in '%.*s'", i, (int)str->m_len, str->m_str); goto not_a_number; } goto finish_parse; skip_remaining_decimal: assert(str->m_str[i] >= '0'); assert(str->m_str[i] <= '9'); result |= CONV_PRECISION_LOSS; for (; i < str->m_len; i++) { if (str->m_str[i] >= '0' && str->m_str[i] <= '9') continue; if (str->m_str[i] == 'e' || str->m_str[i] == 'E') goto parse_exponent_portion; PJ_LOG_WARN("PBNJSON_PNUM_UNXPCTD_CHAR4", 1, PMLOGKFV("CHAR", "%c", str->m_str[i]), "Unexpected character %d('%c') in '%.*s' at %zu", (int)str->m_str[i], str->m_str[i], (int)str->m_len, str->m_str, i); goto not_a_number; } assert(i == str->m_len); goto finish_parse; parse_exponent_portion: assert(exponent >= 0); if (UNLIKELY(str->m_str[i] != 'e' && str->m_str[i] != 'E')) { // problem with the state machine assert(false); PJ_LOG_ERR("PBNJSON_NO_EXPONENT", 1, PMLOGKS("STRING", str->m_str), "Expecting an exponent but didn't get one at %zu in '%.*s'", i, (int)str->m_len, str->m_str); return CONV_GENERIC_ERROR; } i++; switch (str->m_str[i]) { case '-': i++; exponentMultiplier = -1; break; case '+': i++; case '0'...'9': exponentMultiplier = 1; break; default: PJ_LOG_WARN("PBNJSON_PNUM_UNXPCTD_CHAR5", 1, PMLOGKFV("CHAR", "%c", str->m_str[i]), "Unexpected character %d('%c') in '%.*s' at %zu", (int)str->m_str[i], str->m_str[i], (int)str->m_len, str->m_str, i); goto not_a_number; } assert(exponentMultiplier == 1 || exponentMultiplier == -1); for (; i < str->m_len; i++) { switch (str->m_str[i]) { case '0'...'9': if (exponentMultiplier == 1) { if (UNLIKELY(exponent > (INT64_MAX / 10))) goto exponent_overflow; } else if (exponentMultiplier == -1) { if (UNLIKELY(exponent < (INT64_MIN / 10))) goto exponent_overflow; } exponent *= 10; exponent += exponentMultiplier * (str->m_str[i] - '0'); if (exponent * exponentMultiplier < 0) { goto exponent_overflow; } break; default: PJ_LOG_WARN("PBNJSON_PNUM_UNXPCTD_CHAR6", 1, PMLOGKFV("CHAR", "%c", str->m_str[i]), "Unexpected character %d('%c') in '%.*s' at %zu", (int)str->m_str[i], str->m_str[i], (int)str->m_len, str->m_str, i); goto not_a_number; } } assert(i == str->m_len); goto finish_parse; exponent_overflow: // overflow of a 64-bit exponent - +/- infinity or 0 it is. assert(exponent > (INT64_MAX / 10 - 10) || exponent < (INT64_MIN / 10 + 10)); if (exponentMultiplier == 1) { exponent = INT64_MAX; if (integerMultiplier == 1) { *integerPortion = INT64_MAX; result |= CONV_POSITIVE_INFINITY; } else { *integerPortion = INT64_MIN; result |= CONV_NEGATIVE_INFINITY; } } else { result |= CONV_PRECISION_LOSS; exponent = INT64_MIN; *integerPortion = 0; } goto finish_parse; finish_parse: if (trailingZeros) { PJ_LOG_DBG("%"PRId64 " unnecessary 0s in fraction portion of '%.*s'", trailingZeros, (int)str->m_len, str->m_str); } if (fraction == 0) { assert(fractionFactor == 0); } if (*integerPortion == 0 && (decimalPortion == NULL || fraction == 0)) { // shortcut - exponent is redundant if the number is 0.something but we're // ignoring the decimal (or there's no fractional portion) exponent = 0; if (fraction != 0) { result |= CONV_PRECISION_LOSS; } } // can't really do this anyways - it would require us shifting values into or out // of the fractional component when we adjust the integerPortion by the exponent. // internally, we would never use this case anyways because if we care what the // fraction is (i.e. we're converting to a floating point), we'll provide the exponent // pointer anyways if (exponentPortion == NULL && exponent != 0 && fraction != 0) { result |= CONV_PRECISION_LOSS; fraction = 0; fractionFactor = 0; } if (!exponentPortion) { if (*integerPortion != 0) { if (exponent > 0) { while (exponent) { if (*integerPortion > INT64_MAX / 10) { assert(integerMultiplier == 1); result |= CONV_POSITIVE_OVERFLOW; *integerPortion = INT64_MAX; break; } else if (*integerPortion < INT64_MIN / 10) { assert(integerMultiplier == -1); result |= CONV_NEGATIVE_OVERFLOW; *integerPortion = INT64_MIN; break; } if (*integerPortion != 0) assert(*integerPortion * 10 > 0); *integerPortion *= 10; exponent--; } } else if (exponent < 0) { if (fraction) { result |= CONV_PRECISION_LOSS; goto lost_precision; } while (exponent) { if (*integerPortion % 10 != 0) { result |= CONV_PRECISION_LOSS; goto lost_precision; } *integerPortion /= 10; exponent++; } lost_precision: while (exponent++ && *integerPortion > 0) *integerPortion /= 10; } } } else { *exponentPortion = exponent; } if (!decimalPortion) { if (fraction != 0) { result |= CONV_PRECISION_LOSS; } } else { *decimalPortion = fraction; *decimalLeadingZeros = fractionFactor; } return result; not_a_number: return CONV_NOT_A_NUM; fast_stop: if (exponentPortion) *exponentPortion = exponent; if (decimalPortion) *decimalPortion = fraction; if (decimalLeadingZeros) *decimalLeadingZeros = fractionFactor; 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; }
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("PBNJSON_BIG_FILE", 1, PMLOGKS("FILE", file), "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_valid(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("PBNJSON_PARCE_ERR", 3, PMLOGKS("FILE", file), PMLOGKFV("ERRNO", "%d", errno), PMLOGKS("ERROR", err_msg), "Attempt to parse json document '%s' failed (%d) : %s", file, errno, err_msg); free(err_msg); result = jinvalid(); goto return_result; }