Skip to content
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

De-duplicate fmt and format functions and optimize #11596

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ericcurtin
Copy link
Collaborator

@ericcurtin ericcurtin commented Feb 2, 2025

Just use a single function, also only one buffer is required for the operation, no need to copy.

@ericcurtin ericcurtin force-pushed the optimize_fmt branch 2 times, most recently from b0a45a3 to 7435b04 Compare February 2, 2025 15:17
@ericcurtin
Copy link
Collaborator Author

@ngxson PTAL

examples/run/run.cpp Outdated Show resolved Hide resolved
int size2 = vsnprintf(buf.data(), size + 1, fmt, ap2);
std::string buf;
buf.resize(size);
const int size2 = vsnprintf(const_cast<char *>(buf.data()), buf.size() + 1, fmt, ap2);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as we don't modify past the null terminator (or .size())in the std::string, I think it's fine. At least I've done this kinda thing in various codebases for 10 years and it's never been an issue.

I think:

&buf[0]

is also an option.

But if we don't want to do this it's fine, it just seems sub-optimal to create a vector (which is essentially a poor mans std::string) and then copy all the data to a std::string.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks fine for me, but I think the second argument of snprintf should be just buf.size(), without + 1

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested + 1 is correct, it always null terminates, we will be missing a char if we use buf.size()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated string has a length of at most n-1, leaving space for the additional terminating null character.

it always prints a null character, it will truncate output if you specify too short a value you see, buf.size() is one character too short

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The + 1 is fine because the std containers do not explicitly need room for the null terminating character. This is in contrast to plain C string char buf[size] where the string length can be maximum size - 1.

Regarding the UB, I'm also sure that this will always work in the real world, but would still prefer to avoid adding UB to the code (in case this is indeed UB, not sure yet). In this case the performance consideration is not very important, because we use fmt to just print error messages and logs, so it's OK to make the copy.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the micro-optimization in case of UB. Makes the review easier for everyone.

Just use a single function, also only one buffer is required for
the operation, no need to copy.

Signed-off-by: Eric Curtin <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants