From b6fbb035f41ae543ce92e60eb75857bb778e5a2b Mon Sep 17 00:00:00 2001
From: Michael Wetter
Date: Thu, 4 Apr 2024 15:04:40 -0700
Subject: [PATCH] Corrected memory leak and update plug flow example (maint
10.0.x) (#3775)
* Changed interval for #3772
* Corrected memory leak for #3752
---
.../Validation/PlugFlowPipes/PlugFlowAIT.mo | 2 +-
.../Resources/C-Sources/WeeklySchedule.c | 303 ++++++++++++------
.../Resources/C-Sources/WeeklySchedule.h | 15 +-
.../Utilities/IO/Files/WeeklySchedule.mo | 6 +-
Buildings/package.mo | 8 +-
5 files changed, 232 insertions(+), 102 deletions(-)
diff --git a/Buildings/Fluid/FixedResistances/Validation/PlugFlowPipes/PlugFlowAIT.mo b/Buildings/Fluid/FixedResistances/Validation/PlugFlowPipes/PlugFlowAIT.mo
index 84a5bd11137..d8b1779e517 100644
--- a/Buildings/Fluid/FixedResistances/Validation/PlugFlowPipes/PlugFlowAIT.mo
+++ b/Buildings/Fluid/FixedResistances/Validation/PlugFlowPipes/PlugFlowAIT.mo
@@ -375,7 +375,7 @@ equation
annotation (
experiment(
StopTime=603900,
- Interval=900,
+ Interval=120,
Tolerance=1e-006),
__Dymola_Commands(file="modelica://Buildings/Resources/Scripts/Dymola/Fluid/FixedResistances/Validation/PlugFlowPipes/PlugFlowAIT.mos"
"Simulate and plot"),
diff --git a/Buildings/Resources/C-Sources/WeeklySchedule.c b/Buildings/Resources/C-Sources/WeeklySchedule.c
index a01217d8f43..ceb03974290 100644
--- a/Buildings/Resources/C-Sources/WeeklySchedule.c
+++ b/Buildings/Resources/C-Sources/WeeklySchedule.c
@@ -3,6 +3,8 @@
This code implements a weekly schedule.
Changelog:
+ March 30, 2024 by Filip Jorissen, Builtwins
+ Revisions for #1860 to avoid memory leaks when calling ModelicaFormatError.
May 25, 2022 by Michael Wetter, LBNL
Refactored to comply with C89.
March 9, 2022 by Filip Jorissen, KU Leuven
@@ -29,13 +31,11 @@ int cmpfun(const void * tuple1, const void * tuple2) {
return (time1 - time2);
}
-void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t_offset, char* stringData) {
- FILE* fp;
+void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t_offset, const char* stringData) {
const int bufLen = 255;
WeeklySchedule* scheduleID = NULL;
- char* token = NULL;
- struct TimeDataTuple **rules;
+
int i = 0; /* iterator */
int j = 0; /* iterator */
int k = 0; /* iterator */
@@ -48,34 +48,52 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
int comment = 0; /* we are parsing a comment */
int n_rulesInMem = 0; /* number of rules for which we have allocated memory */
int n_rulesInRow = 0; /* number of rules that exist in the current row */
- int n_rowsUnpacked = 0; /* total number of unpacked rules */
int n_rowsPacked = 0; /* number of rules */
int n_newLines = 0; /* number of newlines */
+ int mustHaveNewLine = 0;/* The next character must be a newline */
char c; /* the character that is being parsed in this iteration */
+
int parseToken = 0;
double timeStamp;
- char* buff2;
int tokenLen;
int offset = 0;
- double* lastData = NULL;
-
scheduleID = (WeeklySchedule*)calloc(1, sizeof(WeeklySchedule));
- if ( scheduleID == NULL)
+ if ( scheduleID == NULL){
ModelicaFormatError("Failed to allocate memory for scheduleID in WeeklySchedule.c.");
+ }
- token = (char*)calloc(sizeof(char), bufLen);
- if ( token == NULL)
- ModelicaFormatError("Failed to allocate memory for token in WeeklySchedule.c.");
+ scheduleID->n_allocatedRules = 0;
+ scheduleID->n_allocatedRulesData = 0;
+ scheduleID->lastData = NULL;
+ scheduleID->token = NULL;
+ scheduleID->fp = NULL;
+ scheduleID->buff2 = NULL;
+ scheduleID->rules = NULL;
+
+ scheduleID->token = (char*)calloc(sizeof(char), bufLen);
+ if ( scheduleID->token == NULL){
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
+ ModelicaFormatError("Failed to allocate memory for token in WeeklySchedule.c.");
+ }
if (tableOnFile){
- fp = fopen(name, "r");
- if (fp == NULL) {
+ scheduleID->fp = fopen(name, "r");
+ if (scheduleID->fp == NULL) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Failed to open weekly schedule '%s'.", name);
}
}
+ scheduleID->buff2 = (char*)calloc(sizeof(char), bufLen);
+ if (scheduleID->buff2 == NULL){
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
+ ModelicaFormatError("Failed to allocate memory for buff in WeeklySchedule.c.");
+ }
/* Identify 'tokens' by splitting on (one or more) whitespace characters. */
/* Each token is parsed and special behaviour is created for comments and the header. */
@@ -87,7 +105,7 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
parseToken = 0;
if (tableOnFile){
- c = fgetc( fp ); /* read a character from the file */
+ c = fgetc( scheduleID->fp ); /* read a character from the file */
}else{
c = stringData[j]; /* read a character from the string */
j++;
@@ -95,27 +113,38 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
if ( c == EOF || c == '\0') {
if (!tableOnFile && c == '\0'){
break;
- }else if (tableOnFile && feof(fp)) {
+ }else if (tableOnFile && feof(scheduleID->fp)) {
break; /* exit the while loop */
} else {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Error while reading file '%s'.", name);
}
}{
if (index >= bufLen - 2) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Buffer overflow when reading weekly schedule '%s'.", name);
}
if (c == '\n') { /* Check whether a token ends */
parseToken = 1;
+ mustHaveNewLine = 0;
n_newLines++;
+ } else if (mustHaveNewLine == 1){
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
+ ModelicaFormatError("Error while reading weekly schedule '%s'. Inconsistent line endings: \\r must be followed by \\n.", name);
+ } else if (c == '\r') { /* Check whether a token ends */
+ mustHaveNewLine = 1;
} else if (comment == 1 || c == '#') {
comment = 1;
continue; /* ignore this character and the next characters until a newline is detected, then parse the token */
} else if ( isHeaderLine == 0 && (c == ' ' || c == '\t' ) && index > 0) { /* parse token when reaching a space or tab, unless buffer is empty */
parseToken = 1;
} else if (c != ' ' && c != '\t' ) { /* build up the token by copying a character */
- token[index] = c;
+ scheduleID->token[index] = c;
index++;
}
@@ -123,19 +152,17 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
/* Parse a token if needed. */
if (parseToken == 1 && index > 0) {
/* shouldn't require an overflow check since token is already checked */
- buff2 = (char*)calloc(sizeof(char), bufLen);
- if (buff2 == NULL)
- ModelicaFormatError("Failed to allocate memory for buff in WeeklySchedule.c.");
+
offset = 0;
- token[index] = '\0';
+ scheduleID->token[index] = '\0';
index++;
- tokenLen = strlen(token);
+ tokenLen = strlen(scheduleID->token);
index = 0;
- /* ModelicaFormatWarning("Parsing token %s", token);*/
+ /* ModelicaFormatWarning("Parsing token %s", scheduleID->token); */
- if (foundHeader == 0 && strcmp("double", token) == 0) {
+ if (foundHeader == 0 && strcmp("double", scheduleID->token) == 0) {
/* we found a header line, we expect a specific format after the whitespace */
isHeaderLine = 1;
foundHeader = 1;
@@ -144,105 +171,145 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
int ncharsRow;
int ncharsCol;
- if (strncmp("tab1(", token, 4) != 0) {
+ if (strncmp("tab1(", scheduleID->token, 4) != 0) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect header when reading weekly schedule '%s'. It should start with 'tab1('.", name);
}
- source = token + 5;
+ source = scheduleID->token + 5;
ncharsRow = strcspn(source, ",");
if (tokenLen == ncharsRow + 5 ) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect header when reading weekly schedule '%s'. No comma was found in the header.", name);
}
- strncpy(buff2, source, ncharsRow);
- buff2[ncharsRow] = '\0';
+ if ( ncharsRow > bufLen - 2 ) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
+ ModelicaFormatError("Header length exceeds buffer size.");
+ }
+ strncpy(scheduleID->buff2, source, ncharsRow);
+ scheduleID->buff2[ncharsRow] = '\0';
- if (sscanf(buff2, "%i", &scheduleID->n_rows_in) != 1) {
- ModelicaFormatError("Error in intenger conversion in header while parsing %s in weekly schedule '%s'.", buff2, name);
+ if (sscanf(scheduleID->buff2, "%i", &scheduleID->n_rows_in) != 1) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
+ ModelicaFormatError("Error in intenger conversion in header while parsing %s in weekly schedule '%s'.", scheduleID->buff2, name);
}
source = source + ncharsRow + 1;
ncharsCol = strcspn(source, ")");
if (tokenLen == ncharsCol + ncharsRow + 5 + 1) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect header when reading weekly schedule '%s'. No closing bracket was found in the header.", name);
} else if (tokenLen > ncharsCol + ncharsRow + 5 + 1 + 1) {
- ModelicaFormatError("Incorrect header when reading weekly schedule '%s'. It has trailing characters: '%s'.", name, token + ncharsRow + ncharsCol + 7);
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
+ ModelicaFormatError("Incorrect header when reading weekly schedule '%s'. It has trailing characters: '%s'.", name, scheduleID->token + ncharsRow + ncharsCol + 7);
}
- strncpy(buff2, source, ncharsCol);
- buff2[ncharsCol] = '\0';
- if (sscanf(buff2, "%i", &scheduleID->n_cols_in) != 1) {
- ModelicaFormatError("Error in integer conversion in header while parsing %s in weekly schedule '%s'..", buff2, name);
+ strncpy(scheduleID->buff2, source, ncharsCol);
+ scheduleID->buff2[ncharsCol] = '\0';
+ if (sscanf(scheduleID->buff2, "%i", &scheduleID->n_cols_in) != 1) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
+ ModelicaFormatError("Error in integer conversion in header while parsing %s in weekly schedule '%s'..", scheduleID->buff2, name);
}
if (scheduleID->n_cols_in < 2) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Illegal number of columns '%i' when reading weekly schedule '%s'.", scheduleID->n_cols_in, name);
}
if (scheduleID->n_rows_in < 1) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Illegal number of rows '%i' when reading weekly schedule '%s'.", scheduleID->n_rows_in, name);
}
isHeaderLine = 0;
foundHeader = 1;
- rules = (TimeDataTuple**)calloc(sizeof(TimeDataTuple *), scheduleID->n_rows_in);
- if ( rules == NULL)
+ scheduleID->rules = (TimeDataTuple**)calloc(sizeof(TimeDataTuple *), scheduleID->n_rows_in);
+ if ( scheduleID->rules == NULL){
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Failed to allocate memory for rules in WeeklySchedule.c.");
+ }
n_rulesInMem = scheduleID->n_rows_in;
} else if (foundHeader == 0) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Illegal file format, no header was found when reading weekly schedule '%s'.", name);
} else if (tokensInLine == 0) {
/* 0 tokens have been found on this line, so we're parsing a date/time */
- const int ncharsDays = strcspn(token, ":");
+ const int ncharsDays = strcspn(scheduleID->token, ":");
timeStamp = 0;
if (tokenLen != ncharsDays) {
double val;
- const int ncharsHour = strcspn(token + ncharsDays + 1, ":");
-
- strncpy(buff2, token + ncharsDays + 1, ncharsHour);
- buff2[ncharsHour] = '\0';
- if (sscanf(buff2, "%lf", &val) != 1) {
- ModelicaFormatError("Error in float conversion in hours when reading weekly schedule '%s'. Found token %s with length %i", name, buff2, ncharsHour);
+ const int ncharsHour = strcspn(scheduleID->token + ncharsDays + 1, ":");
+
+ strncpy(scheduleID->buff2, scheduleID->token + ncharsDays + 1, ncharsHour);
+ scheduleID->buff2[ncharsHour] = '\0';
+ if (sscanf(scheduleID->buff2, "%lf", &val) != 1) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
+ ModelicaFormatError("Error in float conversion in hours when reading weekly schedule '%s'. Found token %s with length %i", name, scheduleID->buff2, ncharsHour);
}
if (val > 24 || val < 0) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Unexpected value for hour: '%f' when reading weekly schedule '%s', should be between 0 and 24.", val, name);
}
timeStamp += val * 3600;
if (tokenLen != ncharsHour + ncharsDays + 1) {
- const int ncharsMinutes = strcspn(token + ncharsDays + ncharsHour + 2, ":");
- strncpy(buff2, token + ncharsDays + ncharsHour + 2, ncharsMinutes);
- buff2[ncharsMinutes] = '\0';
- if (sscanf(buff2, "%lf", &val) != 1) {
+ const int ncharsMinutes = strcspn(scheduleID->token + ncharsDays + ncharsHour + 2, ":");
+ strncpy(scheduleID->buff2, scheduleID->token + ncharsDays + ncharsHour + 2, ncharsMinutes);
+ scheduleID->buff2[ncharsMinutes] = '\0';
+ if (sscanf(scheduleID->buff2, "%lf", &val) != 1) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Error in float conversion in minutes when reading weekly schedule '%s'.", name);
}
if (val > 60 || val < 0) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Unexpected value for minute: '%f' when reading weekly schedule '%s', should be between 0 and 60.", val, name);
}
timeStamp += val * 60;
if (tokenLen != ncharsMinutes + ncharsHour + ncharsDays + 2) {
const int ncharsSeconds = tokenLen - ncharsMinutes - ncharsHour - ncharsDays - 2;
- strncpy(buff2, token + ncharsDays + ncharsHour + ncharsMinutes + 3, ncharsSeconds);
- buff2[ncharsSeconds] = '\0';
- if (sscanf(buff2, "%lf", &val) != 1) {
+ strncpy(scheduleID->buff2, scheduleID->token + ncharsDays + ncharsHour + ncharsMinutes + 3, ncharsSeconds);
+ scheduleID->buff2[ncharsSeconds] = '\0';
+ if (sscanf(scheduleID->buff2, "%lf", &val) != 1) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Error in float conversion in seconds when reading weekly schedule '%s'.", name);
}
if (val > 60 || val < 0) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Unexpected value for seconds: '%f' when reading weekly schedule '%s', should be between 0 and 60.", val, name);
}
timeStamp += val;
}
}
}
- strncpy(buff2, token, ncharsDays);
- buff2[ncharsDays] = '\0';
+ strncpy(scheduleID->buff2, scheduleID->token, ncharsDays);
+ scheduleID->buff2[ncharsDays] = '\0';
/* loop over all days (comma separated) and for each date, add a rule */
while ( 1 ) {
- char * startIndex = buff2 + offset;
+ char * startIndex = scheduleID->buff2 + offset;
double t_day, time_i;
int nchars = strcspn(startIndex, ",");
if (nchars != 3 ) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Unexpected day format when reading weekly schedule '%s': %s.", name, startIndex);
}
@@ -261,30 +328,42 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
} else if (strncmp("sun", startIndex, 3) == 0) {
t_day = 6 * 3600 * 24;
} else {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Unexpected day format when parsing weekday '%s': %s.", name, startIndex);
}
/* expand the memory if the initially assigned memory block does not suffice*/
if (rule_i >= n_rulesInMem) {
n_rulesInMem += scheduleID->n_rows_in;
- rules = (TimeDataTuple**)realloc(rules, sizeof(TimeDataTuple*) * n_rulesInMem);
- if (rules == NULL) {
+ scheduleID->rules = (TimeDataTuple**)realloc(scheduleID->rules, sizeof(TimeDataTuple*) * n_rulesInMem);
+ if (scheduleID->rules == NULL) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Failed to reallocate memory when reading weekly schedule '%s'.", name);
}
}
time_i = timeStamp + t_day;
- rules[rule_i] = (TimeDataTuple*)calloc(sizeof(TimeDataTuple), 1);
- if ( rules[rule_i] == NULL)
+ scheduleID->rules[rule_i] = (TimeDataTuple*)calloc(sizeof(TimeDataTuple), 1);
+ if ( scheduleID->rules[rule_i] == NULL){
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Failed to allocate memory for rules[rule_i] in WeeklySchedule.c.");
- rules[rule_i]->time = time_i;
- rules[rule_i]->data = (double*)calloc(sizeof(double), (scheduleID->n_cols_in - 1));
- if ( rules[rule_i]->data == NULL)
+ }
+ scheduleID->n_allocatedRules++;
+
+ scheduleID->rules[rule_i]->time = time_i;
+ scheduleID->rules[rule_i]->data = (double*)calloc(sizeof(double), (scheduleID->n_cols_in - 1));
+ if ( scheduleID->rules[rule_i]->data == NULL){
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Failed to allocate memory for rules[rule_i]->data in WeeklySchedule.c.");
- rule_i++;
+ }
+ scheduleID->n_allocatedRulesData++;
+ rule_i++;
n_rulesInRow++;
- n_rowsUnpacked++;
if (offset == 0) /* only for the first rule in this row*/
n_rowsPacked++;
@@ -300,30 +379,37 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
/* a token has been found on this line before, so we're parsing some numerical data*/
if (tokensInLine >= scheduleID->n_cols_in) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Too many columns on row %i when reading weekly schedule '%s'.", line, name);
}
- if (sscanf(token, "%lf", &val) != 1) {
- if (token[0] == '-') {
+ if (sscanf(scheduleID->token, "%lf", &val) != 1) {
+ if (scheduleID->token[0] == '-') {
val = HUGE_VAL; /*convert the wildcard in a double representation*/
} else {
- ModelicaFormatError("Invalid format for float %s when reading weekly schedule '%s'.", token, name);
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
+ ModelicaFormatError("Invalid format for float %s when reading weekly schedule '%s'.", scheduleID->token, name);
}
}
/* Set the data for all rules that result from this row.*/
for (i = rule_i - n_rulesInRow; i < rule_i; ++i) {
- rules[i]->data[tokensInLine - 1] = val;
+ scheduleID->rules[i]->data[tokensInLine - 1] = val;
}
tokensInLine++;
} else {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Logic error when reading weekly schedule '%s'.", name); /*should not be able to end up here*/
}
- free(buff2);
}
if (c == '\n') { /*reset some internal variables*/
if (tokensInLine > 0 && tokensInLine != scheduleID->n_cols_in) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect number of columns on line %i when reading weekly schedule '%s'.", line, name);
}
line++;
@@ -333,29 +419,37 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
}
}
}
- if (tableOnFile){
- fclose(fp);
- }
if (n_newLines==0){
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("In weekly schedule '%s': The provided %s is incorrectly formatted since it does not contain newline characters.", name, tableOnFile ? "file": "string parameter");
}
if (n_rowsPacked != scheduleID->n_rows_in) {
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect number of rows when reading weekly schedule '%s': %i instead of %i.", name, n_rowsPacked, scheduleID->n_rows_in);
}
- if (scheduleID->n_cols_in < 1)
+ if (scheduleID->n_cols_in < 1){
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("The number of columns in the weekly schedule '%s' is %i, which is too small. ", name, scheduleID->n_cols_in);
+ }
/* sort all data by time stamp*/
- qsort(rules, rule_i, sizeof(TimeDataTuple*), cmpfun);
+ qsort(scheduleID->rules, rule_i, sizeof(TimeDataTuple*), cmpfun);
{
/* working vector with zero initial value*/
- lastData = (double*)calloc(sizeof(double), scheduleID->n_cols_in - 1);
- if (lastData == NULL)
+ scheduleID->lastData = (double*)calloc(sizeof(double), scheduleID->n_cols_in - 1);
+ if (scheduleID->lastData == NULL){
+ weeklyScheduleFreeInit(scheduleID);
+ weeklyScheduleFree(scheduleID);
ModelicaFormatError("Failed to allocate memory for lastData in WeeklySchedule.c., scheduleID->n_cols_in -1 = %d", scheduleID->n_cols_in - 1);
- memset(lastData, (char)(double)0, scheduleID->n_cols_in - 1); /* set vector to zero initial guess*/
+ }
+
+ memset(scheduleID->lastData, (char)(double)0, scheduleID->n_cols_in - 1); /* set vector to zero initial guess*/
/* Loop over all data and fill in wildcards using the last preceeding value.*/
/* This may wrap back to the end of last week, therefore loop the data twice.*/
@@ -364,41 +458,60 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
for (i = 0; i < 2; ++i) {
for (j = 0; j < rule_i; ++j) {
for (k = 0; k < scheduleID->n_cols_in - 1; ++k) {
- if ( rules[j]->data[k] != HUGE_VAL ) {
- lastData[k] = rules[j]->data[k];
+ if ( scheduleID->rules[j]->data[k] != HUGE_VAL ) {
+ scheduleID->lastData[k] = scheduleID->rules[j]->data[k];
} else if (i > 0) {
/* only on the second pass, since otherwise the default value is filled in permanently and
information from the back of the domain can't be recycled */
- rules[j]->data[k] = lastData[k];
+ scheduleID->rules[j]->data[k] = scheduleID->lastData[k];
}
}
}
}
- free(lastData);
}
/* store data for later use */
scheduleID->t_offset = t_offset;
- scheduleID->n_rowsUnpacked = n_rowsUnpacked;
scheduleID->previousIndex = 0;
- scheduleID->schedule = rules;
scheduleID->previousTimestamp = HUGE_VAL;
- free(token);
+ weeklyScheduleFreeInit(scheduleID);
return (void*) scheduleID;
}
+void weeklyScheduleFreeInit(void * ID) {
+ WeeklySchedule* scheduleID = (WeeklySchedule*)ID;
+
+ if (scheduleID->lastData != NULL)
+ free(scheduleID->lastData);
+
+ if (scheduleID->token != NULL)
+ free(scheduleID->token);
+
+ if (scheduleID->fp != NULL)
+ fclose(scheduleID->fp);
+
+ if (scheduleID->buff2 != NULL)
+ free(scheduleID->buff2);
+
+}
+
+
void weeklyScheduleFree(void * ID) {
WeeklySchedule* scheduleID = (WeeklySchedule*)ID;
int i;
- for (i = 0; i < scheduleID->n_rowsUnpacked; ++i) {
- free(scheduleID->schedule[i]->data);
- free(scheduleID->schedule[i]);
+ for (i = 0; i < scheduleID->n_allocatedRulesData; ++i) {
+ free(scheduleID->rules[i]->data);
+ }
+ for (i = 0; i < scheduleID->n_allocatedRules; ++i) {
+ free(scheduleID->rules[i]);
}
- free(scheduleID->schedule);
+ if (scheduleID->rules != NULL)
+ free(scheduleID->rules);
+
free(ID);
ID = NULL;
}
@@ -413,6 +526,8 @@ double getScheduleValue(void * ID, const int column, const double modelicaTime)
int i;
const int columnIndex = column - 1; /* Since we do not store the time indices in the data table */
+ /* Not calling weeklyScheduleFreeInit() or weeklyScheduleFree() since weeklyScheduleFreeInit() has already been called at the end of the
+ initialization and Modelica will call weeklyScheduleFree() upon a call of ModelicaFormatError) */
if (column < 0 || column > scheduleID->n_cols_in - 1) {
ModelicaFormatError("The requested column index '%i' is outside of the table range.", column + 1);
}
@@ -423,28 +538,28 @@ double getScheduleValue(void * ID, const int column, const double modelicaTime)
if (time == scheduleID->previousTimestamp) {
i = scheduleID->previousIndex;
- } else if (time > scheduleID->schedule[scheduleID->previousIndex]->time) {
- for (i = scheduleID->previousIndex; i < scheduleID->n_rowsUnpacked - 1; i ++) {
- if (scheduleID->schedule[i + 1]->time > time) {
+ } else if (time > scheduleID->rules[scheduleID->previousIndex]->time) {
+ for (i = scheduleID->previousIndex; i < scheduleID->n_allocatedRules - 1; i ++) {
+ if (scheduleID->rules[i + 1]->time > time) {
break;
}
}
} else {
for (i = scheduleID->previousIndex; i > 0; i--) {
- if (scheduleID->schedule[i - 1]->time < time) {
+ if (scheduleID->rules[i - 1]->time < time) {
i = i - 1;
break;
}
}
/* if time is smaller than the first row, wrap back to the end of the week */
- if (i == 0 && scheduleID->schedule[0]->time > time) {
- i = scheduleID->n_rowsUnpacked - 1;
+ if (i == 0 && scheduleID->rules[0]->time > time) {
+ i = scheduleID->n_allocatedRules - 1;
}
}
scheduleID->previousIndex = i;
scheduleID->previousTimestamp = time;
- return scheduleID->schedule[i]->data[columnIndex];
+ return scheduleID->rules[i]->data[columnIndex];
}
#endif
diff --git a/Buildings/Resources/C-Sources/WeeklySchedule.h b/Buildings/Resources/C-Sources/WeeklySchedule.h
index 4b52a6db2e9..3a4105e2610 100644
--- a/Buildings/Resources/C-Sources/WeeklySchedule.h
+++ b/Buildings/Resources/C-Sources/WeeklySchedule.h
@@ -16,6 +16,7 @@
#ifndef WEEKCAL_h
#define WEEKCAL_h
+
typedef struct TimeDataTuple {
double time; /* Time relative to monday midnight. */
double *data; /* Corresponding column data */
@@ -26,20 +27,28 @@ typedef struct WeeklySchedule {
double t_offset; /* Time offset for monday, midnight. */
int n_rows_in; /* Number of input rows */
int n_cols_in; /* Number of input columns */
- int n_rowsUnpacked; /* Number of rows: number of rows after unpacking the date */
double previousTimestamp; /* Time where the schedule was called the previous time */
int previousIndex; /* Index where the schedule was called the previous time */
- struct TimeDataTuple ** schedule;
+ int n_allocatedRules;
+ int n_allocatedRulesData;
+ double * lastData;
+ char * token;
+ FILE* fp;
+ char* buff2;
+ struct TimeDataTuple **rules;
+
} WeeklySchedule;
-void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t_offset, char* stringData);
+void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t_offset, const char* stringData);
void weeklyScheduleFree(void * ID);
+void weeklyScheduleFreeInit(void * ID);
+
double getScheduleValue(void * ID, const int column, const double time);
#endif
diff --git a/Buildings/Utilities/IO/Files/WeeklySchedule.mo b/Buildings/Utilities/IO/Files/WeeklySchedule.mo
index a4155579eed..9b06d00db0d 100644
--- a/Buildings/Utilities/IO/Files/WeeklySchedule.mo
+++ b/Buildings/Utilities/IO/Files/WeeklySchedule.mo
@@ -54,6 +54,10 @@ protected
revisions="
-
+March 30 2024, by Filip Jorissen:
+Avoiding memory leaks for IBPSA, #1860.
+
+-
April 10 2022, by Filip Jorissen:
Added parameter source implementation.
@@ -90,7 +94,7 @@ The parameter columns
is used to specify which columns of the table
The first column is time, hence for the above example, set columns = {2}
.
-See Buildings/Resources/Data/schedule.txt
+See Buildings/Resources/Data/schedule.txt
for an example of the supported file format.
"),
diff --git a/Buildings/package.mo b/Buildings/package.mo
index f80428885fb..6d7b8273b11 100644
--- a/Buildings/package.mo
+++ b/Buildings/package.mo
@@ -299,12 +299,14 @@ have been improved in a
Buildings, #3659.
-xxx
+ |
Buildings.Utilities.IO.Files
|
-xxx
+ |
Buildings.Utilities.IO.Files.WeeklySchedule
|
- xxx.
+ | Corrected C-code implementation to avoid memory leak.
+ This is for
+ IBPSA, #1860.
|