While studying STD algorithms in C++, one simple exercise I did was masking an email address. Turning *johndoe@emailprovider.tld* into *j*****e@emailprovider.tld*, considering various cases like very short emails and incorrect ones (one could impose a precondition on the input, that it must be a valid email address to provide a valid output, but for this exercise, I wanted some edge cases).

To know what kinds of inputs I’m dealing with and what the corresponding valid outputs should be, I’ll start with the test data:

const std::map<std::string, std::string> tests{ {"johndoe@emailprovider.tld", "j*****e@emailprovider.tld"}, {"jde@emailprovider.tld", "j*e@emailprovider.tld"}, {"jd@emailprovider.tld", "**@emailprovider.tld"}, {"j@emailprovider.tld", "*@emailprovider.tld"}, {"@emailprovider.tld", "@emailprovider.tld"}, {"wrong", "w***g"}, {"wro", "w*o"}, {"wr", "**"}, {"w", "*"}, {"", ""}, {"@", "@"}, };

Besides solving the task itself, I was also curious about an aspect: What would be the differences between an implementation using no STD algorithms and one using various STD algorithms? I followed how the code looks and how it performs.

The first approach was the classic one, using a single iteration of the input string, during which each character is checked to see if it should be copied to the output as is or it should be masked. After the iteration, if the character *@* was not found, the propper transformation is done.

std::string mask(const std::string &email, const char mask) { if (email[0] == '@') { return email; } std::string masked; masked.reserve(email.size()); bool hide = true; bool is_email = false; for (size_t i = 0; i < email.size(); ++i) { if (email[i] == '@') { is_email = true; hide = false; if (i > 2) { masked[0] = email[0]; masked[i - 1] = email[i - 1]; } } masked += hide ? mask : email[i]; } if (!is_email && masked.size() > 2) { masked[0] = email[0]; masked[masked.size() - 1] = email[masked.size() - 1]; } return masked; }