/* timing safe bytes / ASCII unicode equal function
 *
 * The function leaks when both objects aren't of the same length or when an
 * error has occured.
 *
 * Return:
 *    1: if both objects are equal
 *    0: if unequal
 *   -1: if an error has occured (not bytes or compact ASCII unicode)
 */

int
timingsafe_eq(PyObject *a, PyObject *b)
{
	const unsigned char *va=NULL;
	const unsigned char *vb=NULL;

	Py_ssize_t length=-1;
	Py_ssize_t i;
	Py_ssize_t result;

	if (PyBytes_CheckExact(a) && PyBytes_CheckExact(b)) {
		length = PyBytes_GET_SIZE(a);
		if (length != PyBytes_GET_SIZE(b)) {
			return 0;
		}
		va = (const unsigned char *)PyBytes_AS_STRING(a);
		vb = (const unsigned char *)PyBytes_AS_STRING(b);
	}
	else if(PyUnicode_CheckExact(a) && PyUnicode_CheckExact(b)) {
	    if (PyUnicode_READY(a) == -1 || PyUnicode_READY(b) == -1) {
	        assert(0 && "unicode_eq ready fail");
	        return -1;
	    }
		if (!PyUnicode_IS_COMPACT_ASCII(a) || !PyUnicode_IS_COMPACT_ASCII(b)) {
			return -1;
		}
		length = PyUnicode_GET_LENGTH(a);
		if (length != PyUnicode_GET_LENGTH(b)) {
			return 0;
		}
		va = (const unsigned char *)_PyUnicode_COMPACT_DATA(a);
		vb = (const unsigned char *)_PyUnicode_COMPACT_DATA(b);
    }
	else {
		// not exact bytes or unicode
		return -1;
	}

	assert(length != -1 && "length not set");
	assert(va && "va not set");
	assert(vb && "vb not set");

	/* don't optimize for length == 0 */
	result = 0;
	for (i=0; i < length; i++) {
		result |= *va++ ^ *vb++;
	}
	return (result == 0);
}
