Skip to content

Commit

Permalink
Implement use of memccpy()
Browse files Browse the repository at this point in the history
- The POSIX/C23 function `memccpy()` provides benefits that combat downfalls of other C-provided copying functions, e.g., `strcpy()` and `strcat()`
	* One of these downfalls is the quadratic runtime when trying to concatenate two strings using `strcat()`

- New macro within `SW_Defines.h`
	* As previously mentioned, the `memccpy()` function is a POSIX standard or contained within C23
	* The program now checks to see these conditions are met, and if they are we set the macro `sw_memccpy()` to `memccpy()`
	* Otherwise, if neither of these conditions are provided, we use the new custom SOILWAT2 function - `sw_memccpy_custom()`
		- This function provides the same functionality (see article link below)
		- This function also makes use of the compiler macro '__restrict' which is supported by Clang and GCC
			* It is not a C++ standard to have a 'restrict' keyword

- Replaced all instances of `strcpy()` with `sw_memccpy()` (other than the instance in `Str_Dup()`
- Replaced all instances of `strcat()` with `sw_memccpy()`
- Replaced simple instances of `snprintf()` with `sw_memccpy()`
	* A simple instance refers to the use of `snprintf()` to write a single string that is not formatted (e.g., snprintf(..., ..., "%s", ...)) or similar

- `MkDir()` gains new possible warning message
* This occurs when the buffer fills and the directory is created, but by another name than what was expectedc

** For more information see https://developers.redhat.com/blog/2019/08/12/efficient-string-copying-and-concatenation-in-c#choosing_a_solution
  • Loading branch information
N1ckP3rsl3y committed Aug 6, 2024
1 parent fa7d059 commit e0557d1
Show file tree
Hide file tree
Showing 14 changed files with 516 additions and 134 deletions.
8 changes: 8 additions & 0 deletions include/SW_Defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,14 @@ typedef struct timespec WallTimeSpec;
typedef time_t WallTimeSpec;
#endif

/* Memory copying via `sw_memccpy()` and SOILWAT2's custom function */
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) || \
(defined(__STDC__) && defined(__STDC_VERSION__) && \
__STDC_VERSION__ >= 202311L)
#define sw_memccpy memccpy
#else
#define sw_memccpy sw_memccpy_custom
#endif

/* =================================================== */
/* RNG structs */
Expand Down
3 changes: 3 additions & 0 deletions include/myMemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ void Mem_Set(void *block, byte c, size_t n);

void Mem_Copy(void *dest, const void *src, size_t n);

void *sw_memccpy_custom(
void *__restrict dest, void *__restrict src, int c, size_t n
);

#ifdef __cplusplus
}
Expand Down
11 changes: 6 additions & 5 deletions src/SW_Domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
#include "include/SW_Domain.h" // for SW_DOM_CheckProgress, SW_DOM_Cre...
#include "include/filefuncs.h" // for LogError, CloseFile, key_to_id
#include "include/generic.h" // for swTRUE, LOGERROR, swFALSE, Bool
#include "include/myMemory.h" // for sw_memccpy_custom
#include "include/SW_datastructs.h" // for SW_DOMAIN, LOG_INFO
#include "include/SW_Defines.h" // for LyrIndex, LARGE_VALUE, TimeInt
#include "include/SW_Files.h" // for SW_F_deconstruct, SW_F_deepCopy
#include "include/SW_Output.h" // for ForEachOutKey
#include "include/Times.h" // for yearto4digit, Time_get_lastdoy_y
#include <stdio.h> // for sscanf, FILE
#include <stdlib.h> // for strtod, strtol
#include <string.h> // for strcmp, memcpy, strcpy, memset
#include <string.h> // for strcmp, memcpy, memset

#if defined(SWNETCDF)
#include "include/SW_netCDF.h"
Expand Down Expand Up @@ -268,8 +269,8 @@ void SW_DOM_read(SW_DOMAIN *SW_Domain, LOG_INFO *LogInfo) {
);
goto closeFile;
}
(void) snprintf(
SW_Domain->DomainType, sizeof SW_Domain->DomainType, "%s", value
(void) sw_memccpy(
SW_Domain->DomainType, value, '\0', sizeof SW_Domain->DomainType
);
break;
case 1: // Number of X slots
Expand Down Expand Up @@ -333,8 +334,8 @@ void SW_DOM_read(SW_DOMAIN *SW_Domain, LOG_INFO *LogInfo) {
goto closeFile;
}

(void) snprintf(
SW_Domain->crs_bbox, sizeof SW_Domain->crs_bbox, "%s", value
(void) sw_memccpy(
SW_Domain->crs_bbox, value, '\0', sizeof SW_Domain->crs_bbox
);
break;
case 9: // Minimum x coordinate
Expand Down
6 changes: 3 additions & 3 deletions src/SW_Files.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#include "include/SW_Defines.h" // for MAX_FILENAMESIZE
#include <stdio.h> // for FILENAME_MAX, NULL, FILE, stderr
#include <stdlib.h> // for free
#include <string.h> // for strcpy, strcmp, strlen, memcpy
#include <string.h> // for memccpy, strcmp, strlen, memcpy

/* =================================================== */
/* Global Function Definitions */
Expand All @@ -66,7 +66,7 @@ void SW_CSV_F_INIT(const char *s, LOG_INFO *LogInfo) {
DirName(s, dirString);

if (DirExists(dirString)) {
(void) snprintf(inbuf, sizeof inbuf, "%s", s);
(void) sw_memccpy(inbuf, (char *) s, '\0', sizeof inbuf);
if (!RemoveFiles(inbuf, LogInfo)) {
LogError(
LogInfo, LOGWARN, "Can't remove old csv output file: %s\n", s
Expand Down Expand Up @@ -348,7 +348,7 @@ void SW_F_construct(
c = dirString;

if (c) {
strcpy(SW_ProjDir, c);
(void) sw_memccpy(SW_ProjDir, c, '\0', sizeof dirString);
c = localfirstfile;
p = c + strlen(SW_ProjDir);
while (*p) {
Expand Down
2 changes: 1 addition & 1 deletion src/SW_Main_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include "include/SW_Defines.h" // for MAX_MSGS, MAX_LOG_SIZE, BUILD_DATE
#include <stdio.h> // for fprintf, stderr, fflush, stdout
#include <stdlib.h> // for exit, free, EXIT_FA...
#include <string.h> // for strcpy, strncmp
#include <string.h> // for strncmp

#ifdef RSOILWAT
#include <R.h> // for error(), and warning() from <R_ext/Error.h>
Expand Down
201 changes: 171 additions & 30 deletions src/SW_Output.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ See the \ref out_algo "output algorithm documentation" for details.
#include "include/SW_VegProd.h" // for echo_VegProd
#include "include/Times.h" // for Time_days_in_month, WKDAYS
#include <stdio.h> // for snprintf, fprintf, printf
#include <string.h> // for strcat, strcmp, strcpy, memset
#include <string.h> // for strcmp, memccpy, memset

// Array-based output declarations:
#if defined(SW_OUTARRAY) || defined(SWNETCDF)
Expand Down Expand Up @@ -2161,7 +2161,9 @@ void SW_OUT_set_colnames(
for (i = 0; i < ncol_OUT[eSW_Temp]; i++) {
if (i < 3) {
// Normal air temperature columns
(void) snprintf(ctemp, sizeof ctemp, "%s", cnames_eSW_Temp[i]);
(void) sw_memccpy(
ctemp, (char *) cnames_eSW_Temp[i], '\0', sizeof ctemp
);
} else {
// Surface temperature columns
(void) snprintf(
Expand Down Expand Up @@ -2540,7 +2542,7 @@ void SW_OUT_set_colnames(
}
#endif
i = 0;
(void) snprintf(ctemp, sizeof ctemp, "%s", "fCover_BareGround");
(void) sw_memccpy(ctemp, "fCover_BareGround", '\0', sizeof ctemp);
colnames_OUT[eSW_Biomass][i] = Str_Dup(ctemp, LogInfo);
if (LogInfo->stopRun) {
return; // Exit function prematurely due to error
Expand Down Expand Up @@ -3216,9 +3218,39 @@ void SW_OUT_write_today(
int i;
int outPeriod;

#ifdef SW_OUTTEXT
char *resPtr = NULL;

char *soilWritePtr[SW_OUTNPERIODS] = {
sw->FileStatus.buf_soil[0],
sw->FileStatus.buf_soil[1],
sw->FileStatus.buf_soil[2],
sw->FileStatus.buf_soil[3]
};
char *regWritePtr[SW_OUTNPERIODS] = {
sw->FileStatus.buf_reg[0],
sw->FileStatus.buf_reg[1],
sw->FileStatus.buf_reg[2],
sw->FileStatus.buf_reg[3]
};
#endif

#ifdef STEPWAT
Bool use_help_txt;
Bool use_help_SXW;

char *soilAggWritePtr[SW_OUTNPERIODS] = {
sw->FileStatus.buf_soil[0],
sw->FileStatus.buf_soil[1],
sw->FileStatus.buf_soil[2],
sw->FileStatus.buf_soil[3]
};
char *regAggWritePtr[SW_OUTNPERIODS] = {
sw->FileStatus.buf_reg[0],
sw->FileStatus.buf_reg[1],
sw->FileStatus.buf_reg[2],
sw->FileStatus.buf_reg[3]
};
#endif


Expand Down Expand Up @@ -3280,6 +3312,37 @@ void SW_OUT_write_today(
// formatting functions `get_XXX`, and concatenate for one row of
// `csv`-output
ForEachOutKey(k) {
#ifdef SW_OUTTEXT
size_t writeSizeReg[SW_OUTNPERIODS] = {
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN)
};

size_t writeSizeSoil[SW_OUTNPERIODS] = {
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN)
};
#endif

#ifdef STEPWAT
size_t writeSizeSoilAgg[SW_OUTNPERIODS] = {
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN)
};
size_t writeSizeRegAgg[SW_OUTNPERIODS] = {
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN),
(size_t) (MAX_LAYERS * OUTSTRLEN)
};
#endif

#ifdef SWDEBUG
if (debug) {
sw_printf("key=%d=%s: ", k, key2str[k]);
Expand All @@ -3291,6 +3354,7 @@ void SW_OUT_write_today(
}

for (i = 0; i < OutDom->used_OUTNPERIODS; i++) {

outPeriod = OutDom->timeSteps[k][i];
use_help = (Bool) (outPeriod != eSW_NoTime && writeit[outPeriod]);

Expand Down Expand Up @@ -3376,22 +3440,66 @@ void SW_OUT_write_today(
#if defined(SW_OUTTEXT)
/* concatenate formatted output for one row of `csv`- files */
if (OutDom->print_SW_Output) {
strcpy(tempstr, sw->OutRun.sw_outstr);
(void) sw_memccpy(
tempstr,
sw->OutRun.sw_outstr,
'\0',
(size_t) (MAX_LAYERS * OUTSTRLEN)
);

if (OutDom->has_sl[k]) {
strcat(sw->FileStatus.buf_soil[outPeriod], tempstr);
resPtr = (char *) sw_memccpy(
soilWritePtr[outPeriod],
tempstr,
'\0',
writeSizeSoil[outPeriod]
);
soilWritePtr[outPeriod] = resPtr - 1;
writeSizeSoil[outPeriod] -=
(resPtr - soilWritePtr[outPeriod] - 1);
} else {
strcat(sw->FileStatus.buf_reg[outPeriod], tempstr);
resPtr = (char *) sw_memccpy(
regWritePtr[outPeriod],
tempstr,
'\0',
writeSizeReg[outPeriod]
);
regWritePtr[outPeriod] = resPtr - 1;
writeSizeReg[outPeriod] -=
(resPtr - regWritePtr[outPeriod] - 1);
}
}


#ifdef STEPWAT
if (OutDom->print_IterationSummary) {
strcpy(tempstr, sw->OutRun.sw_outstr_agg);
(void) sw_memccpy(
tempstr,
sw->OutRun.sw_outstr_agg,
'\0',
(size_t) (MAX_LAYERS * OUTSTRLEN)
);

if (OutDom->has_sl[k]) {
strcat(sw->FileStatus.buf_soil_agg[outPeriod], tempstr);
resPtr = sw_memccpy(
soilAggWritePtr[outPeriod],
tempstr,
'\0',
writeSizeSoilAgg[outPeriod]
);
soilAggWritePtr[outPeriod] = resPtr - 1;
writeSizeSoilAgg[outPeriod] -=
(resPtr - soilAggWritePtr[outPeriod] - 1);
} else {
strcat(sw->FileStatus.buf_reg_agg[outPeriod], tempstr);
resPtr = sw_memccpy(
regAggWritePtr[outPeriod],
tempstr,
'\0',
writeSizeRegAgg[outPeriod]
);
regAggWritePtr[outPeriod] = resPtr - 1;
writeSizeRegAgg[outPeriod] -=
(resPtr - regAggWritePtr[outPeriod] - 1);
}
}
#endif
Expand Down Expand Up @@ -3608,39 +3716,72 @@ void SW_OUT_close_files(

void echo_outputs(SW_OUT_DOM *OutDom) {
int k;
int index;
int writeValIndex = 0;
char str[OUTSTRLEN];
char errstr[MAX_ERROR];
size_t writeSize = MAX_ERROR;
char *writePtr = errstr;
char *resPtr = NULL;
char *cpyPtr = NULL;
const int numWriteStrs = 6;
const size_t errHeaderSize = 75;

const char *errStrHeader = "---------------------------\nKey ";

const char *errStrFooter =
"\n---------- End of Output Configuration ---------- \n";

(void) snprintf(
errstr,
sizeof errstr,
"%s",
const char *errStrConf =
"\n===============================================\n"
" Output Configuration:\n"
);
" Output Configuration:\n";

char *writeStrs[] = {
(char *) errStrHeader,
(char *) key2str[0], /* Overwrite in loop below */
(char *) "\n\tSummary Type: ",
(char *) styp2str[0], /* Overwrite in loop below */
(char *) "\n\tStart period: %d",
(char *) "\n\tEnd period : %d\n"
};

TimeInt writeVals[] = {
OutDom->first_orig[0], /* Overwrite in loop below */
OutDom->last_orig[0] /* Overwrite in loop below */
};

(void) sw_memccpy(errstr, (char *) errStrConf, '\0', errHeaderSize);


ForEachOutKey(k) {
if (!OutDom->use[k]) {
if (OutDom->use[k]) {
writeStrs[1] = (char *) key2str[k];
writeStrs[3] = (char *) styp2str[OutDom->sumtype[k]];

writeVals[0] = OutDom->first_orig[k];
writeVals[0] = OutDom->last_orig[k];
} else {
continue;
}
strcat(errstr, "---------------------------\nKey ");
strcat(errstr, key2str[k]);
strcat(errstr, "\n\tSummary Type: ");
strcat(errstr, styp2str[OutDom->sumtype[k]]);
(void) snprintf(
str, OUTSTRLEN, "\n\tStart period: %d", OutDom->first_orig[k]
);
strcat(errstr, str);
(void) snprintf(
str, OUTSTRLEN, "\n\tEnd period : %d", OutDom->last_orig[k]
);
strcat(errstr, str);
strcat(errstr, "\n");

for (index = 0; index < numWriteStrs; index++) {
cpyPtr = writeStrs[index];

if (index > 4) {
(void
) snprintf(str, OUTSTRLEN, cpyPtr, writeVals[writeValIndex]);

writeValIndex++;
cpyPtr = str;
}

resPtr = (char *) sw_memccpy(writePtr, cpyPtr, '\0', writeSize);
writeSize -= (resPtr - writePtr - 1);
writePtr = resPtr - 1;
}
}

strcat(errstr, "\n---------- End of Output Configuration ---------- \n");
sw_memccpy(writePtr, (char *) errStrFooter, '\0', writeSize);
printf("%s\n", errstr);
}

Expand Down
Loading

0 comments on commit e0557d1

Please sign in to comment.