JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_readBytes()

in src/main/c/Posix/SerialPort_Posix.c [965:1050]


JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_readBytes(JNIEnv *env, jobject obj, jlong serialPortPointer, jbyteArray buffer, jint bytesToRead, jint offset, jint timeoutMode, jint readTimeout)
{
	// Ensure that a positive number of bytes was passed in to read
	jsize bufferLength = (*env)->GetArrayLength(env, buffer);
	serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
	if ((bytesToRead < 0) || (offset < 0) || (bufferLength < offset))
		return -1;

	// Fetch a pointer to the underlying data buffer
	int numBytesRead = -1, numBytesReadTotal = offset, ioctlResult = 0;
	int bytesRemaining = ((bytesToRead + offset) > bufferLength) ? (bufferLength - offset) : bytesToRead;
	jbyte *readBuffer = (*env)->GetByteArrayElements(env, buffer, NULL);
	if (checkJniError(env, __LINE__ - 1) || !readBuffer)
		return -1;

	// Infinite blocking mode specified, don't return until we have completely finished the read
	if (((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0) && (readTimeout == 0))
	{
		// While there are more bytes we are supposed to read
		while (bytesRemaining > 0)
		{
			// Attempt to read some number of bytes from the serial port
			port->errorLineNumber = __LINE__ + 1;
			do { errno = 0; numBytesRead = read(port->handle, readBuffer + numBytesReadTotal, bytesRemaining); port->errorNumber = errno; } while ((numBytesRead < 0) && (errno == EINTR));
			if ((numBytesRead == -1) || ((numBytesRead == 0) && (ioctl(port->handle, FIONREAD, &ioctlResult) == -1)))
			{
				// If all bytes were not successfully read, it is an error
				numBytesRead = -1;
				break;
			}

			// Fix index variables
			numBytesReadTotal += numBytesRead;
			bytesRemaining -= numBytesRead;
		}
	}
	else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0)		// Blocking mode, but not indefinitely
	{
		// Get current system time
		struct timespec expireTime, currTime;
		clock_gettime(CLOCK_MONOTONIC, &expireTime);
		expireTime.tv_sec += (readTimeout / 1000);
		expireTime.tv_nsec += ((readTimeout % 1000) * 1000000);
		if (expireTime.tv_nsec >= 1000000000)
		{
			expireTime.tv_sec += 1;
			expireTime.tv_nsec -= 1000000000;
		}

		// While there are more bytes we are supposed to read and the timeout has not elapsed
		do
		{
			port->errorLineNumber = __LINE__ + 1;
			do { errno = 0; numBytesRead = read(port->handle, readBuffer + numBytesReadTotal, bytesRemaining); port->errorNumber = errno; } while ((numBytesRead < 0) && (errno == EINTR));
			if ((numBytesRead == -1) || ((numBytesRead == 0) && (ioctl(port->handle, FIONREAD, &ioctlResult) == -1)))
			{
				// If any bytes were read, return those bytes
				if (!numBytesReadTotal)
					numBytesRead = -1;
				break;
			}

			// Fix index variables
			numBytesReadTotal += numBytesRead;
			bytesRemaining -= numBytesRead;

			// Get current system time
			clock_gettime(CLOCK_MONOTONIC, &currTime);
		} while ((bytesRemaining > 0) && ((expireTime.tv_sec > currTime.tv_sec) || ((expireTime.tv_sec == currTime.tv_sec) && (expireTime.tv_nsec > currTime.tv_nsec))));
	}
	else		// Semi- or non-blocking specified
	{
		// Read from the port
		port->errorLineNumber = __LINE__ + 1;
		do { errno = 0; numBytesRead = read(port->handle, readBuffer + numBytesReadTotal, bytesRemaining); port->errorNumber = errno; } while ((numBytesRead < 0) && (errno == EINTR));
		if ((numBytesRead == -1) || ((numBytesRead == 0) && (ioctl(port->handle, FIONREAD, &ioctlResult) == -1)))
			numBytesRead = -1;
		else
			numBytesReadTotal += numBytesRead;
	}

	// Return number of bytes read if successful
	(*env)->ReleaseByteArrayElements(env, buffer, readBuffer, (numBytesRead == -1) ? JNI_ABORT : 0);
	checkJniError(env, __LINE__ - 1);
	return (numBytesRead == -1) ? -1 : (numBytesReadTotal - offset);
}