-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix memory issues #16
Changes from all commits
c1c6b66
89fb00f
93e910b
3075c3c
1517b5e
2cc64ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,8 +8,8 @@ file(GLOB_RECURSE SRC src/*.cpp) | |
add_library(DCCEXProtocol STATIC ${SRC}) | ||
add_library(DCCEX::Protocol ALIAS DCCEXProtocol) | ||
|
||
# Requires at least C++11 | ||
target_compile_features(DCCEXProtocol PUBLIC cxx_std_11) | ||
# Stuck at C++11 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixes the C++ standard to 11 |
||
target_compile_options(DCCEXProtocol PRIVATE -std=c++11) | ||
|
||
# Don't bother users with warnings by setting 'SYSTEM' | ||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) | ||
|
@@ -36,13 +36,7 @@ endforeach() | |
|
||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SRC}) | ||
|
||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) | ||
include(CTest) | ||
if(PROJECT_IS_TOP_LEVEL) | ||
add_subdirectory(docs) | ||
endif() | ||
|
||
if(BUILD_TESTING | ||
AND CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME | ||
AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME) | ||
add_subdirectory(tests) | ||
endif() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,20 @@ | ||
macro(sanitize SANITIZERS) | ||
# Set in current scope | ||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${SANITIZERS}") | ||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${SANITIZERS}") | ||
set(LDFLAGS "${LDFLAGS} -fsanitize=${SANITIZERS}") | ||
|
||
# Set in PARENT_SCOPE | ||
get_directory_property(HAS_PARENT_SCOPE PARENT_DIRECTORY) | ||
if(HAS_PARENT_SCOPE) | ||
set(CMAKE_C_FLAGS | ||
"${CMAKE_C_FLAGS} -fsanitize=${SANITIZERS}" | ||
${CMAKE_C_FLAGS} | ||
PARENT_SCOPE) | ||
set(CMAKE_CXX_FLAGS | ||
"${CMAKE_CXX_FLAGS} -fsanitize=${SANITIZERS}" | ||
${CMAKE_CXX_FLAGS} | ||
PARENT_SCOPE) | ||
set(LDFLAGS | ||
"${LDFLAGS} -fsanitize=${SANITIZERS}" | ||
${LDFLAGS} | ||
PARENT_SCOPE) | ||
endif() | ||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${SANITIZERS}") | ||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${SANITIZERS}") | ||
set(LDFLAGS "${LDFLAGS} -fsanitize=${SANITIZERS}") | ||
endmacro() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,21 +48,28 @@ enum splitState : byte { | |
COMPLETE_i_COMMAND | ||
}; | ||
|
||
int16_t DCCEXInbound::_maxParams; | ||
int16_t DCCEXInbound::_parameterCount; | ||
byte DCCEXInbound::_opcode; | ||
int32_t *DCCEXInbound::_parameterValues; | ||
char *DCCEXInbound::_cmdBuffer; | ||
int16_t DCCEXInbound::_maxParams = 0; | ||
int16_t DCCEXInbound::_parameterCount = 0; | ||
byte DCCEXInbound::_opcode = 0; | ||
int32_t *DCCEXInbound::_parameterValues = nullptr; | ||
char *DCCEXInbound::_cmdBuffer = nullptr; | ||
|
||
// Public methods | ||
|
||
void DCCEXInbound::setup(int16_t maxParameterValues) { | ||
_parameterValues = (int32_t *)malloc(maxParameterValues * sizeof(int32_t)); | ||
_parameterValues = (int32_t *)realloc(_parameterValues, maxParameterValues * sizeof(int32_t)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better to realloc in case this function gets called multiple times |
||
_maxParams = maxParameterValues; | ||
_parameterCount = 0; | ||
_opcode = 0; | ||
} | ||
|
||
void DCCEXInbound::cleanup() { | ||
if (_parameterValues) { | ||
free(_parameterValues); | ||
_parameterValues = nullptr; | ||
} | ||
} | ||
|
||
byte DCCEXInbound::getOpcode() { return _opcode; } | ||
|
||
int16_t DCCEXInbound::getParameterCount() { return _parameterCount; } | ||
|
@@ -81,19 +88,18 @@ bool DCCEXInbound::isTextParameter(int16_t parameterNumber) { | |
return _isTextInternal(parameterNumber); | ||
} | ||
|
||
char *DCCEXInbound::getText(int16_t parameterNumber) { | ||
char *DCCEXInbound::getTextParameter(int16_t parameterNumber) { | ||
if (parameterNumber < 0 || parameterNumber >= _parameterCount) | ||
return 0; | ||
if (!_isTextInternal(parameterNumber)) | ||
return 0; | ||
return _cmdBuffer + (_parameterValues[parameterNumber] & ~QUOTE_FLAG_AREA); | ||
; | ||
} | ||
|
||
char *DCCEXInbound::getSafeText(int16_t parameterNumber) { | ||
char *unsafe = getText(parameterNumber); | ||
char *DCCEXInbound::copyTextParameter(int16_t parameterNumber) { | ||
char *unsafe = getTextParameter(parameterNumber); | ||
if (!unsafe) | ||
return unsafe; // bad parameter number probabaly | ||
return unsafe; // bad parameter number probably | ||
char *safe = (char *)malloc(strlen(unsafe) + 1); | ||
strcpy(safe, unsafe); | ||
return safe; | ||
|
@@ -203,10 +209,10 @@ void DCCEXInbound::dump(Print *out) { | |
|
||
for (int i = 0; i < getParameterCount(); i++) { | ||
if (isTextParameter(i)) { | ||
out->print(F("getText(")); | ||
out->print(F("getTextParameter(")); | ||
out->print(i); | ||
out->print(F(")=\"")); | ||
out->print(getText(i)); | ||
out->print(getTextParameter(i)); | ||
out->println('"'); | ||
} else { | ||
out->print(F("getNumber(")); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,6 +62,14 @@ DCCEXProtocol::DCCEXProtocol(int maxCmdBuffer) { | |
_bufflen = 0; | ||
} | ||
|
||
DCCEXProtocol::~DCCEXProtocol() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add destructor to cleanup memory |
||
// Free memory for command buffer | ||
delete[] (_cmdBuffer); | ||
|
||
// Cleanup command parser | ||
DCCEXInbound::cleanup(); | ||
} | ||
|
||
// Set the delegate instance for callbacks | ||
void DCCEXProtocol::setDelegate(DCCEXProtocolDelegate *delegate) { this->_delegate = delegate; } | ||
|
||
|
@@ -153,11 +161,11 @@ void DCCEXProtocol::requestServerVersion() { | |
|
||
bool DCCEXProtocol::receivedVersion() { return _receivedVersion; } | ||
|
||
int DCCEXProtocol::getMajorVersion() { return _majorVersion; } | ||
int DCCEXProtocol::getMajorVersion() { return _version[0]; } | ||
|
||
int DCCEXProtocol::getMinorVersion() { return _minorVersion; } | ||
int DCCEXProtocol::getMinorVersion() { return _version[1]; } | ||
|
||
int DCCEXProtocol::getPatchVersion() { return _patchVersion; } | ||
int DCCEXProtocol::getPatchVersion() { return _version[2]; } | ||
|
||
unsigned long DCCEXProtocol::getLastServerResponseTime() { return _lastServerResponseTime; } | ||
|
||
|
@@ -572,7 +580,7 @@ void DCCEXProtocol::_processCommand() { | |
} | ||
} else if (DCCEXInbound::getNumber(0) == 'P') { // Receive turntable position info | ||
if (DCCEXInbound::getParameterCount() == 5 && | ||
DCCEXInbound::isTextParameter(4)) { // Turntable position index enry | ||
DCCEXInbound::isTextParameter(4)) { // Turntable position index entry | ||
_processTurntableIndexEntry(); | ||
} | ||
} else if (DCCEXInbound::getNumber(0) == 'R') { // Receive roster info | ||
|
@@ -618,57 +626,50 @@ void DCCEXProtocol::_processCommand() { | |
} | ||
|
||
void DCCEXProtocol::_processServerDescription() { //<iDCCEX version / microprocessorType / MotorControllerType / | ||
//buildNumber> | ||
// buildNumber> | ||
// console->println(F("processServerDescription()")); | ||
if (_delegate) { | ||
char *description; | ||
description = (char *)malloc(strlen(DCCEXInbound::getSafeText(0)) + 1); | ||
sprintf(description, "%s", DCCEXInbound::getText(0)); | ||
int versionStartAt = 7; // e.g. "DCC-EX V-" | ||
char *temp = _nextServerDescriptionParam(description, versionStartAt, true); | ||
_majorVersion = atoi(temp); | ||
versionStartAt = versionStartAt + strlen(temp) + 1; | ||
temp = _nextServerDescriptionParam(description, versionStartAt, true); | ||
_minorVersion = atoi(temp); | ||
versionStartAt = versionStartAt + strlen(temp) + 1; | ||
temp = _nextServerDescriptionParam(description, versionStartAt, true); | ||
_patchVersion = atoi(temp); | ||
char *description{DCCEXInbound::getTextParameter(0) + 7}; | ||
int *version = _version; | ||
|
||
while (description < _cmdBuffer + _maxCmdBuffer) { | ||
// Delimiter | ||
char const delim = *description++; | ||
if (delim != '-' && delim != '.') | ||
continue; | ||
|
||
// Int | ||
char const first_digit = *description; | ||
if (!isdigit(first_digit)) | ||
continue; | ||
|
||
// string to int | ||
int const v = atoi(description); | ||
if (v < 0) | ||
return; // Error | ||
else if (v < 10) | ||
description += 1; | ||
else if (v < 100) | ||
description += 2; | ||
else if (v < 1000) | ||
description += 3; | ||
else | ||
return; // Error | ||
|
||
// Done after 3 numbers | ||
*version++ = v; | ||
if (version - _version >= 3) | ||
break; | ||
} | ||
|
||
_receivedVersion = true; | ||
_delegate->receivedServerVersion(_majorVersion, _minorVersion, _patchVersion); | ||
_delegate->receivedServerVersion(_version[0], _version[1], _version[2]); | ||
} | ||
// console->println(F("processServerDescription(): end")); | ||
} | ||
|
||
void DCCEXProtocol::_processMessage() { //<m "message"> | ||
_delegate->receivedMessage(DCCEXInbound::getSafeText(0)); | ||
} | ||
|
||
char *DCCEXProtocol::_nextServerDescriptionParam(char *description, int startAt, bool lookingAtVersionNumber) { | ||
char _tempString[MAX_SERVER_DESCRIPTION_PARAM_LENGTH]; | ||
int i = 0; | ||
size_t j; | ||
bool started = false; | ||
for (j = startAt; j < strlen(description) && i < (MAX_SERVER_DESCRIPTION_PARAM_LENGTH - 1); j++) { | ||
if (started) { | ||
if (description[j] == ' ' || description[j] == '\0') | ||
break; | ||
if (lookingAtVersionNumber && (description[j] == '-' || description[j] == '.')) | ||
break; | ||
_tempString[i] = description[j]; | ||
i++; | ||
} else { | ||
if (description[j] == ' ') | ||
started = true; | ||
if (lookingAtVersionNumber && (description[j] == '-' || description[j] == '.')) | ||
started = true; | ||
} | ||
} | ||
_tempString[i] = '\0'; | ||
char *_result; | ||
_result = (char *)malloc(strlen(_tempString)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line caused an out of bounce access by the way |
||
sprintf(_result, "%s", _tempString); | ||
// console->println(_result); | ||
return _result; | ||
_delegate->receivedMessage(DCCEXInbound::getTextParameter(0)); | ||
} | ||
|
||
// Consist/loco methods | ||
|
@@ -781,8 +782,8 @@ void DCCEXProtocol::_processRosterEntry() { //<jR id ""|"desc" ""|"funct1/funct2 | |
// console->println(F("processRosterEntry()")); | ||
// find the roster entry to update | ||
int address = DCCEXInbound::getNumber(1); | ||
char *name = DCCEXInbound::getSafeText(2); | ||
char *funcs = DCCEXInbound::getSafeText(3); | ||
char *name = DCCEXInbound::copyTextParameter(2); | ||
char *funcs = DCCEXInbound::copyTextParameter(3); | ||
bool missingRosters = false; | ||
|
||
Loco *loco = roster->getByAddress(address); | ||
|
@@ -854,7 +855,7 @@ void DCCEXProtocol::_processTurnoutEntry() { | |
// find the turnout entry to update | ||
int id = DCCEXInbound::getNumber(1); | ||
bool thrown = (DCCEXInbound::getNumber(2) == 'T'); | ||
char *name = DCCEXInbound::getSafeText(3); | ||
char *name = DCCEXInbound::copyTextParameter(3); | ||
bool missingTurnouts = false; | ||
|
||
Turnout *t = turnouts->getById(id); | ||
|
@@ -938,7 +939,7 @@ void DCCEXProtocol::_processRouteEntry() { | |
// find the Route entry to update | ||
int id = DCCEXInbound::getNumber(1); | ||
RouteType type = (RouteType)DCCEXInbound::getNumber(2); | ||
char *name = DCCEXInbound::getSafeText(3); | ||
char *name = DCCEXInbound::copyTextParameter(3); | ||
bool missingRoutes = false; | ||
|
||
Route *r = routes->getById(id); | ||
|
@@ -1008,7 +1009,7 @@ void DCCEXProtocol::_processTurntableEntry() { // <jO id type position position_ | |
TurntableType ttType = (TurntableType)DCCEXInbound::getNumber(2); | ||
int index = DCCEXInbound::getNumber(3); | ||
int indexCount = DCCEXInbound::getNumber(4); | ||
char *name = DCCEXInbound::getSafeText(5); | ||
char *name = DCCEXInbound::copyTextParameter(5); | ||
|
||
Turntable *tt = turntables->getById(id); | ||
if (tt) { | ||
|
@@ -1040,7 +1041,7 @@ void DCCEXProtocol::_processTurntableIndexEntry() { // <jP id index angle "[desc | |
int ttId = DCCEXInbound::getNumber(1); | ||
int index = DCCEXInbound::getNumber(2); | ||
int angle = DCCEXInbound::getNumber(3); | ||
char *name = DCCEXInbound::getSafeText(4); | ||
char *name = DCCEXInbound::copyTextParameter(4); | ||
if (index == 0) { // Index 0 is always home, and never has a label, so set one | ||
sprintf(name, "Home"); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ctest swallows errors thrown by LeakSanitizer, call test executable directly