示例#1
0
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;
}
示例#2
0
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;
}
示例#3
0
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;
}
示例#4
0
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;
}
示例#5
0
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;
	}
}
示例#6
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;
}
示例#7
0
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;
}
示例#8
0
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;
}
示例#9
0
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;
}
示例#11
0
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;
}
示例#12
0
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;
}
示例#13
0
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;

}
示例#14
0
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;
}
示例#15
0
//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;
}
示例#18
0
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;
}
示例#19
0
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;
}