@@ -400,6 +400,25 @@ bool FirmataParser::bufferDataAtPosition(const uint8_t data, const size_t pos)
400
400
return bufferOverflow;
401
401
}
402
402
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
+
403
422
/* *
404
423
* Process incoming sysex messages. Handles REPORT_FIRMWARE and STRING_DATA internally.
405
424
* Calls callback function for STRING_DATA and all other sysex messages.
@@ -410,39 +429,25 @@ void FirmataParser::processSysexMessage(void)
410
429
switch (dataBuffer[0 ]) { // first byte in buffer is command
411
430
case REPORT_FIRMWARE:
412
431
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]);
421
442
}
422
- bufferDataAtPosition (' \0 ' , i); // Terminate the string
423
- (*currentReportFirmwareCallback)(currentReportFirmwareCallbackContext, sv_major, sv_minor, (const char *)&dataBuffer[0 ]);
424
443
}
425
444
break ;
426
445
case STRING_DATA:
427
446
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]);
446
451
}
447
452
break ;
448
453
default :
0 commit comments