@@ -400,6 +400,25 @@ bool FirmataParser::bufferDataAtPosition(const uint8_t data, const size_t pos)
400400 return bufferOverflow;
401401}
402402
403+ /* *
404+ * Transform 7-bit firmata message into 8-bit stream
405+ * @param bytec The encoded data byte length of the message (max: 16383).
406+ * @param bytev A pointer to the encoded array of data bytes.
407+ * @return The length of the decoded data.
408+ * @note The conversion will be done in place on the provided buffer.
409+ * @private
410+ */
411+ size_t FirmataParser::decodeByteStream (size_t bytec, uint8_t * bytev) {
412+ size_t decoded_bytes, i;
413+
414+ for ( i = 0 , decoded_bytes = 0 ; i < bytec ; ++decoded_bytes, ++i ) {
415+ bytev[decoded_bytes] = bytev[i];
416+ bytev[decoded_bytes] |= (uint8_t )(bytev[++i] << 7 );
417+ }
418+
419+ return decoded_bytes;
420+ }
421+
403422/* *
404423 * Process incoming sysex messages. Handles REPORT_FIRMWARE and STRING_DATA internally.
405424 * Calls callback function for STRING_DATA and all other sysex messages.
@@ -410,39 +429,25 @@ void FirmataParser::processSysexMessage(void)
410429 switch (dataBuffer[0 ]) { // first byte in buffer is command
411430 case REPORT_FIRMWARE:
412431 if (currentReportFirmwareCallback) {
413- size_t sv_major = dataBuffer[1 ], sv_minor = dataBuffer[2 ];
414- size_t i = 0 , j = 3 ;
415- while (j < sysexBytesRead) {
416- // The string length will only be at most half the size of the
417- // stored input buffer so we can decode the string within the buffer.
418- bufferDataAtPosition (dataBuffer[j], i);
419- ++i;
420- ++j;
432+ const size_t major_version_offset = 1 ;
433+ const size_t minor_version_offset = 2 ;
434+ const size_t string_offset = 3 ;
435+ // Test for malformed REPORT_FIRMWARE message (used to query firmware prior to Firmata v3.0.0)
436+ if ( 3 > sysexBytesRead ) {
437+ (*currentReportFirmwareCallback)(currentReportFirmwareCallbackContext, 0 , 0 , (const char *)NULL );
438+ } else {
439+ const size_t end_of_string = (string_offset + decodeByteStream ((sysexBytesRead - string_offset), &dataBuffer[string_offset]));
440+ bufferDataAtPosition (' \0 ' , end_of_string); // NULL terminate the string
441+ (*currentReportFirmwareCallback)(currentReportFirmwareCallbackContext, (size_t )dataBuffer[major_version_offset], (size_t )dataBuffer[minor_version_offset], (const char *)&dataBuffer[string_offset]);
421442 }
422- bufferDataAtPosition (' \0 ' , i); // Terminate the string
423- (*currentReportFirmwareCallback)(currentReportFirmwareCallbackContext, sv_major, sv_minor, (const char *)&dataBuffer[0 ]);
424443 }
425444 break ;
426445 case STRING_DATA:
427446 if (currentStringCallback) {
428- size_t bufferLength = (sysexBytesRead - 1 ) / 2 ;
429- size_t i = 1 , j = 0 ;
430- while (j < bufferLength) {
431- // The string length will only be at most half the size of the
432- // stored input buffer so we can decode the string within the buffer.
433- bufferDataAtPosition (dataBuffer[i], j);
434- ++i;
435- bufferDataAtPosition ((dataBuffer[j] + (dataBuffer[i] << 7 )), j);
436- ++i;
437- ++j;
438- }
439- // Make sure string is null terminated. This may be the case for data
440- // coming from client libraries in languages that don't null terminate
441- // strings.
442- if (dataBuffer[j - 1 ] != ' \0 ' ) {
443- bufferDataAtPosition (' \0 ' , j);
444- }
445- (*currentStringCallback)(currentStringCallbackContext, (const char *)&dataBuffer[0 ]);
447+ const size_t string_offset = 1 ;
448+ const size_t end_of_string = (string_offset + decodeByteStream ((sysexBytesRead - string_offset), &dataBuffer[string_offset]));
449+ bufferDataAtPosition (' \0 ' , end_of_string); // NULL terminate the string
450+ (*currentStringCallback)(currentStringCallbackContext, (const char *)&dataBuffer[string_offset]);
446451 }
447452 break ;
448453 default :
0 commit comments