Converting integer to time format - c++

I am looking for a nice way to convert an int into a time format. For example, I take in the integer 460 and it returns 5:00, or the integer 1432 and it returns 14:32. The only way I could think of would be tediously turning it into a string, breaking it into two strings, and checking both strings for correctness.
Thank you.

As I pointed out in my comment, I think your representation is highly problematic. I propose that you represent everything as seconds, and use some simple calculations for parsing the minutes/hours.
class PlayTime {
size_t sec;
public:
PlayTime(size_t hours = 0, size_t minutes = 0, size_t seconds = 0)
: sec(hours*60*60 + minutes*60 + seconds) {}
size_t hours() const { return sec/(60*60); }
size_t minutes() const { return (sec/60)%60; }
size_t seconds() const { return sec%60; }
PlayTime & operator +=(const PlayTime &that) {
this-> sec += that.sec;
return *this;
}
};
PlayTime operator+(PlayTime a, Playtime b) { return a+=b; }

For easy compatibility and portability, you might want to look at the standard C runtime library functions for managing a time_t. It represents time in seconds since 1970-01-01T00:00:00 +0000 UTC.
Now that you have posted your intent (4 minutes 32 seconds stored as 432), be aware that simple calculations using this format are not straightforward. For example, what is 30 seconds later from 4:32? It would appear to be 462.

Since your examples aren't quite precise, it's hard to give a straight answer. You haven't said how would you want to store the time after conversion. Do you have some kind of class, or just wanna store it in a string? If the latter, you'll probably use a stringstream:
unsigned int number = 460;
std::stringstream parser;
if((number/100)+(number%100)/60<10) parser << "0"; // trailing 0 if minutes < 10
parser << (number/100)+(number%100)/60 // minutes
<< ':'; // separator
if((number%100)%60<10) parser << "0"; // trailing 0 if seconds < 10
parser << (number%100)%60; // seconds
std::string time = parser.str();
Note hovewer that that's not the best way to do it. C++ provides a <ctime> header which includes the tm struct, and it would be better if you used that instead.

Related

Date string to epoch seconds (UTC)

Question
I want to parse a date-time given as string (UTC) into seconds since epoch. Example (see EpochConverter):
2019-01-15 10:00:00 -> 1547546400
Problem
The straightforward solution, which is also accepted in a very related question C++ Converting a time string to seconds from the epoch goes std::string -> std::tm -> std::time_t using std::get_time and then std::mktime:
std::tm tm;
std::stringstream ss("2019-01-15 10:00:00");
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
std::time_t epoch = std::mktime(&tm);
// 1547546400 (expected)
// 1547539200 (actual, 2 hours too early)
But std::mktime seems to mess up the hours due to timezone. I am executing the code from UTC+01:00, but we also had DST at that date, so its +2 here.
The tm shows 15 for the hour field after std::get_time. It gets messed up as soon as it enters std::mktime.
So again, the string is to be interpreted as UTC timestamp, no timezones whatsoever should be involved. But all solutions I came up with seem to interpret it as local timestamp and add offsets to it.
Restrictions
I have some restrictions for this:
C++17
platform/compiler independent
no environment variable hacking
no external libraries (like boost)
Feel free to post answers involving those for the sake of Q&A though, but I wont accept them.
Research
I found various attempts to solve this problem, but none met my requirements:
std::mktime (as mentioned above), messes up the time because it assumes local time
strptime, not available on my platform, not part of the standard
timegm (thats exactly what I would need), not platform independent
_mkgmtime, not platform independent
boost::posix_time::from_iso_string, is an external library
std::chrono::date::parse, not available with C++17
clear and reset the timezone variable with tzset, uses environment variable hacking
manually countering the offset with mktime(localtime(&timestamp)) - mktime(gmtime(&timestamp)), computes the wrong offset since it does not account for DST (1 hour on my platform but it would need to be 2 hours)
Solution prior to C++20: Roll your own.
Given the right documentation, it really is much easier than it sounds, and can even be lightning fast if you don't need much error detection.
The first problem is to parse the numbers without manipulating any of them. You only need to read unsigned values of length 2 and 4 digits, so just do that bare minimum:
int
read2(std::string const& str, int pos)
{
return (str[pos] - '0')*10 + (str[pos+1] - '0');
}
int
read4(std::string const& str, int pos)
{
return (str[pos] - '0')*1000 + (str[pos+1] - '0')*100 +
(str[pos+2] - '0')*10 + (str[pos+3] - '0');
}
Now given a string, it is easy to parse out the different values you will need:
// yyyy-mm-dd hh:MM:ss -> count of non-leap seconds since 1970-01-01 00:00:00 UTC
// 0123456789012345678
long long
EpochConverter(std::string const& str)
{
auto y = read4(str, 0);
auto m = read2(str, 5);
auto d = read2(str, 8);
...
The part that usually trips people up is how to convert the triple {y, m, d} into a count of days since/prior 1970-01-01. Here is a collection of public domain calendrical algorithms that will help you do this. This is not a 3rd party date/time library. It is a tutorial on the algorithms you will need to write your own date/time library. And these algorithms are efficient. No iteration. No large tables. That makes them very pipeline and cache friendly. And they are unit tested over a span of +/- a million years. So you don't have to worry about hitting any correctness boundaries with them. These algorithms also have a very in-depth derivation if you are interested in how they work.
So just go to the collection of public domain calendrical algorithms, pick out the algorithms you need (and customize them however you want), and roll your own converter.
For example:
#include <cstdint>
#include <limits>
#include <string>
int
days_from_civil(int y, unsigned m, unsigned d) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
y -= m <= 2;
const int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<int>(doe) - 719468;
}
int
read2(std::string const& str, int pos)
{
return (str[pos] - '0')*10 + (str[pos+1] - '0');
}
int
read4(std::string const& str, int pos)
{
return (str[pos] - '0')*1000 + (str[pos+1] - '0')*100 +
(str[pos+2] - '0')*10 + (str[pos+3] - '0');
}
// yyyy-mm-dd hh:MM:ss -> count of non-leap seconds since 1970-01-01 00:00:00 UTC
// 0123456789012345678
long long
EpochConverter(std::string const& str)
{
auto y = read4(str, 0);
auto m = read2(str, 5);
auto d = read2(str, 8);
auto h = read2(str, 11);
auto M = read2(str, 14);
auto s = read2(str, 17);
return days_from_civil(y, m, d)*86400LL + h*3600 + M*60 + s;
}
#include <iostream>
int
main()
{
std::cout << EpochConverter("2019-01-15 10:00:00") << '\n';
}
This just output for me:
1547546400
Sprinkle in whatever error detection is appropriate for your application.
I had the same requirement recently. I was disappointed to find that the handling of DST and timezones seemed inconsistent between writing timestamps and parsing them.
The code I came up with was this:
void time_point_from_stream(std::istream &is, system_clock::time_point &tp)
{
std::tm tm {};
is >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
// unhappily, mktime thinks it's reading local time with DST adjustments
auto my_time_t = std::mktime(&tm);
my_time_t += tm.tm_gmtoff;
if (tm.tm_isdst == 1)
my_time_t -= 3600;
tp = system_clock::from_time_t(my_time_t);
if (not is)
return;
auto ch = is.peek();
if (std::isspace(ch))
return;
if (ch == '.')
{
double zz;
is >> zz;
auto zseconds = std::chrono::duration< double >(zz);
tp += chrono::duration_cast< system_clock::duration >(zseconds);
if (not is)
return;
ch = is.peek();
}
if (ch == 'Z')
is.get();
else if (not isspace(ch))
{
is.setstate(std::ios::failbit);
}
}
Essentially, the steps are:
Use std::get_time to fill a tm
use std::mktime to convert that to a time_t
reverse out timezone and DST adjustments
convert to a std::chrono::system_clock::time_point
Parse the fractional seconds and adjust the result.
I believe c++20 improves on the situation.
Howard Hinnant has also written an improved date/time library. There is also boost::posix_time which I have always found easier to use than the std offering.

What's a good data structure for a 24hr minute-by-minute boolean record

I am tasked to create a data structure that holds a boolean for every minute of the last 24hrs. (Did event X occur?) I need to keep the last 24hrs all the time. (That is, data will constantly be added, old data popped off.) The data is to be persisted to a flash drive. We are on an embedded platform, but memory isn't that limited (I have 128MB available), fragmentation might become a problem, though. This is a realtime system, but since the record is per minute, there's little runtime constraints.
The interface could look something like this:
class x_record {
public:
// record whether or not x occurred this minute
void record_entry(bool x_occured);
// how many minutes has x occured in the last 24hrs?
unsigned int x_occurance_minutes() const;
private:
// HERE BE DRAGONS
};
What would be a good data structure to store the actual data in? My current favorites are std::deque<bool> and an array of 24 long long, with 60 of their 64 bits each used for the 60mins of an hour. The latter is the current favorite for persistence.
I think I have a pretty good idea about the pros and cons of both ideas, but would hope some of you could provide additional insides and maybe even more ideas.
P.S.: This is strictly C++03 + TR1 + Boost 1.52, no C++11/14 available.
To elaborate on the vector<bool> version, I think it's quite a good idea, since you always store the same amount of data (that's at least what I understood):
class x_record {
vector<bool> data;
unsigned int currentMinute;
public:
x_record(): data(24*60, false), currentMinute(0){}
// record whether or not x occurred this minute
void record_entry(bool x_occured){
data[currentMinute] = x_occured;
currentMinute = (currentMinute+1)%data.size();
}
};
This way, the vector size is constant so it shouldn't be fragmented (since it's allocated all at the same time). You can keep track of the current minute with the currentMinute variable. When you fill all the fields, you just set it to 0 with %(24*60) and overwrite the old data, since you don't need it.
You could also use a normal array instead of a vector<bool>, but that would require either more space (since normally C++ stores bool values the same way as a char), or some bit manipulation which is - in my opinion - reinventing the wheel, when we got the vector<bool> specialization.
A circular buffer:
int countBits(std::uint32_t v) {
// source: http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
typedef std::uint32_t T;
v = v - ((v >> 1) & (T)~(T)0/3); // temp
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * CHAR_BIT; // count
}
class x_record {
public:
x_record() { std::memset(&history, 0, sizeof(history)); }
// record whether or not x occurred this minute
void record_entry(bool x_occured) {
uint64_t mask = 1 << (bit % 32);
uint64_t set = x_occured ? mask : 0;
history[bit / 32] = (history[bit / 32] & ~mask) | set;
bit = (bit + 1) % (24*60);
}
// how many minutes has x occured in the last 24hrs?
int x_occurance_minutes() const {
int count = 0;
for (int i=0; i<45; ++i) {
count += countBits(history[i]);
}
return count;
}
private:
std::uint32_t history[45];
short bit = 0;
};
I would have a std::vector<bool> for every hour and only ditch hourly. So you could have a std::deque<std::vector<bool> >. Likewise it could be a std::deque<long long> but I don't see a benefit as compared to the vector.
It makes things efficient, easy to understand and less error prone.
As suggested in the comments, std::bitset might be a good choice. It's a fixed-size sequence of bits that can be manipulated independently. It takes less memory than a std::vector<bool> (even thought you said this is not a problem for you). You might however need to wrap it in another container if you need to make the sequence circular, so that you always keep the last 24*60 minutes, not the 24*60 minutes of a day.
When you are only concerned on how often the event occurred in the last 24 hours, and can completely neglect the time when it occurred, you can simply record the occurences.
Consider the following (not tested):
class x_record {
public:
// record whether or not x occurred this minute
void record_entry(bool x_occured) {
if (x_occured) {
m_records.insert(getTime());
}
}
// how many minutes has x occured in the last 24hrs?
unsigned int x_occurance_minutes() {
clearRecords();
return m_records.size();
}
private:
time_t getTime() const {
return time(NULL) / 60; // Get Minute time stamp
}
void clearRecords() {
// Erase all records that happend before the last 24 hours
time_t frameStart = getTime() - 60*60*24;
for (std::set<time_t>::iterator it = m_recods.begin(); it != m_records.end(); ++it) {
if (*it < frameStart) {
m_records.erase(it);
} else {
break;
}
}
}
private:
std::set<time_t> m_records;
};
This is suitable best if events occur sparsely.
It uses the constrain that sets store their elements in a strict weak ordering, so that elements with lower time stamp will be listed first. Also
You should consider a different key type for inserting into the set, as time_t is not guaranteed to represent seconds.
I would suggest a class containing boost.dynamic_bitset, a counter for handling the storing of the new value and a conversion function for accessing via hour/minute.
The dynamic_set handles most of the requirements:
boost, no C++11
compact
handles 24*60 bits
has a count() function for counting set bits
returns blocks for storing as bits
doesn't have the "container" issues of std::vector
As was suggested by Marius Bancila first in a comment and then in an answer (please go and upvote that answer, he gave the hint that turned out to be the solution), std::bitset<> is ideal for this. However, since his answer is rather vague, I decided to post a more concrete description of what I ended up using:
class x_record {
public:
void record_entry(bool x_occured) {
record_ <<= 1;
record_[0] = x_occurred;
}
unsigned int x_occurance_minutes() const {
return record_.count();
}
void write_to(std::ostream& os) const {
os << m_overload_minutes.to_string();
}
void read_from(std::istream& is) const {
std::string bits;
is >> std::setw(minutes_to_keep) >> bits;
if( !is || bits.size()!=minutes_to_keep )
throw std::runtime_error("invalid file format");
record_ = std::bitset<60*24>(bits);
}
private:
std::bitset<60*24> record_;
};
As you can see, std::bitset<> is pretty much exactly what I needed. Also, persisting the data is very easy.
Of course, in reality, this was a bit more complex1, but in principle this is indeed the whole thing.
Thanks to everyone for trying to help me!
1 It turned out that it was easier to call x_occurance_minutes() every few msecs, for which record_.count() seemed quite some overhead, so I called it in record_entry() (called once per minute only) instead and cached the result.

Code performance between if else statements and length of string

I'm writing code that takes a number from a user and prints in back in letters as string. I want to know, which is better performance-wise, to have if statements, like
if (n < 100) {
// code for 2-digit numbers
} else if (n < 1000) {
// code for 3-digit numbers
} // etc..
or to put the number in a string and get its length, then work on it as a string.
The code is written in C++.
Of course if-else will be faster.
To compare two numbers you just compare them bitwise (there are different ways to do it but it's a very fast operation).
To get the length of the string you will need to make the string, put the data into it and compute the length somehow (there can be different ways of doing it too, the simplest being counting all the symbols). Of course it takes much more time.
On a simple example though you will not notice any difference. It often amazes me that people get concerned with such things (no offense). It will not make any difference for you if the code will execute in 0.003 seconds instead of 0.001 seconds really... You should make such low-level optimizations only after you know that this exact place is a bottleneck of your application, and when you are sure that you can increase the performance by a decent amount.
Until you measure and this really is a bottleneck, don't worry about performance.
That said, the following should be even faster (for readability, let's assume you use a type that ranges between 0 and 99999999):
if (n < 10000) {
// code for less or equal to 4 digits
if (n < 100)
{
//code for less or equal to 2 digits
if (n < 10)
return 1;
else
return 2;
}
else
{
//code for over 2 digits, but under or equal to 4
if (n>=1000)
return 4;
else
return 3;
}
} else {
//similar
} // etc..
Basically, it's a variation of binary search. Worst case, this will take O(log(n)) as opposed to O(n) - n being the maximum number of digits.
The string variant will be slower:
std::stringstream ss; // allocation, initialization ...
ss << 4711; // parsing, setting internal flags, ...
std::string str = ss.str(); // allocations, array copies ...
// cleaning up (compiler does it for you) ...
str.~string();
ss.~stringstream(); // destruction ...
The ... indicate there's more stuff happening.
A compact (good for cache) loop (good for branch prediction) might be what you want:
int num_digits (int value, int base=10) {
int num = 0;
while (value) {
value /= base;
++num;
}
return num;
}
int num_zeros (int value, int base=10) {
return num_decimal_digits(value, base) - 1;
}
Depending on circumstances, because it is cache and prediction friendly, this may be faster than solutions based on relational operators.
The templated variant enables the compiler to do some micro optimizations for your division:
template <int base=10>
int num_digits (int value) {
int num = 0;
while (value) {
value /= base;
++num;
}
return num;
}
The answers are good, but think a bit, about relative times.
Even by the slowest method you can think of, the program can do it in some tiny fraction of a second, like maybe 100 microseconds.
Balance that against the fastest user you can imagine, who could type in the number in maybe 500 milliseconds, and who could read the output in another 500 milliseconds, before doing whatever comes next.
OK, the machine does essentially nothing for 1000 milliseconds, and in the middle it has to crunch like crazy for 100 microseconds because, after all, we don't want the user to think the program is slow ;-)

Checking if time_t is between other time_t with some margin of error

I have two time_t variables: timeA and timeB.
What I want to do is check if timeA is the same as timeB. However, I know that in some cases they won't be exactly the same and there may be a 1 or 2 seconds difference between the two of them, so what I really want to check is:
if (timeB - 2sec) <= timeA <= (timeB + 2sec)
Is it possible to do so?
I suppose one option is not to use time_t but instead keep timeB as a tm struct, and just before the comparison, subtract two seconds and create a time_t timeBminus and then add four seconds and create time_t timeBplus. The problem is that I will be comparing several millions of timeA - timeB pairs and want to keep it as simple and fast as possible.
How can I do it?
Something like -
if (std::fabs(std::difftime(timeA, timeB)) < 2.0)
(Not able to check the exact type etc from here but I think the idea is correct)
Even though time_t usually represents seconds, it is in fact not a standard behavior. The details of time_t are left to implementation (According to this)
Instead of using the time_ts directly, it is best to use struct timespec and use the tv_sec member. To check whether they are in 2s distance from each other:
static inline bool close_enough(struct timespec &ts1, struct timespec &ts2)
{
const int64_t sec_in_nsec = 1000000000ll;
int64_t ts1_nsec = ts1.tv_sec * sec_in_nsec + ts1.tv_nsec;
int64_t ts2_nsec = ts2.tv_sec * sec_in_nsec + ts2.tv_nsec;
return ts1_nsec >= ts2_nsec - 2 * sec_in_nsec && ts1_nsec <= ts2_nsec + 2 * sec_in_nsec;
}
or use the same expression in an if. The function call will be optimized away, so no worries about that. Using abs as suggested in other answers is fine, even though it converts your integers into floating points and performs floating point operations.
Update
Using time_t, you can use difftime to get the difference between two times. Again:
static inline bool close_enough(time_t &t1, time_t &t2)
{
double d = difftime(t1, t2);
return d >= -2 && d <= 2;
}
if(abs(timeA - timeB) <= 2) {
// yay!
}
If your compiler supports c++11 you should use chrono instead of ctime:
#include <iostream>
#include <chrono>
#include <tuple>
int main() {
std::chrono::system_clock::time_point a,b;
std::tie(a,b) = std::minmax(a,b);
if ((b-a) < std::chrono::seconds(2)) {
}
}
This way you know the difference between the times is actually less than 2 seconds rather than simply 2 ticks of the clock, where ticks aren't necessarily seconds at all.
Unfortunately there's no std::abs for clock durations, or you would just write if (std::abs(b-a) < std::chrono::seconds(2)), although it's easy enough to write your own:
template<typename Rep,typename Period>
std::chrono::duration<Rep,Period> abs(std::chrono::duration<Rep,Period> d)
{
return std::chrono::duration<Rep,Period>(std::abs(d.count()));
}

Is there a way to improve the speed or efficiency of this lookup? (C/C++)

I have a function I've written to convert from a 64-bit integer to a base 62 string. Originally, I achieved this like so:
char* charset = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
int charsetLength = strlen(charset);
std::string integerToKey(unsigned long long input)
{
unsigned long long num = input;
string key = "";
while(num)
{
key += charset[num % charsetLength];
num /= charsetLength;
}
return key;
}
However, this was too slow.
I improved the speed by providing an option to generate a lookup table. The table is about 624 strings in size, and is generated like so:
// Create the integer to key conversion lookup table
int lookupChars;
if(lookupDisabled)
lookupChars = 1;
else
largeLookup ? lookupChars = 4 : lookupChars = 2;
lookupSize = pow(charsetLength, lookupChars);
integerToKeyLookup = new char*[lookupSize];
for(unsigned long i = 0; i < lookupSize; i++)
{
unsigned long num = i;
int j = 0;
integerToKeyLookup[i] = new char[lookupChars];
while(num)
{
integerToKeyLookup[i][j] = charset[num % charsetLength];
num /= charsetLength;
j++;
}
// Null terminate the string
integerToKeyLookup[i][j] = '\0';
}
The actual conversion then looks like this:
std::string integerToKey(unsigned long long input)
{
unsigned long long num = input;
string key = "";
while(num)
{
key += integerToKeyLookup[num % lookupSize];
num /= lookupSize;
}
return key;
}
This improved speed by a large margin, but I still believe it can be improved. Memory usage on a 32-bit system is around 300 MB, and more than 400 MB on a 64-bit system. It seems like I should be able to reduce memory and/or improve speed, but I'm not sure how.
If anyone could help me figure out how this table could be further optimized, I'd greatly appreciate it.
Using some kind of string builder rather than repeated concatenation into 'key' would provide a significant speed boost.
You may want to reserve memory in advance for your string key. This may get you a decent performance gain, as well as a gain in memory utilization. Whenever you call the append operator on std::string, it may double the size of the internal buffer if it has to reallocate. This means each string may be taking up significantly more memory than is necessary to store the characters. You can avoid this by reserving memory for the string in advance.
I agree with Rob Walker - you're concentrating on improving performance in the wrong area. The string is the slowest part.
I timed the code (your original is broken, btw) and your original (when fixed) was 44982140 cycles for 100000 lookups and the following code is about 13113670.
const char* charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
#define CHARSET_LENGTH 62
// maximum size = 11 chars
void integerToKey(char result[13], unsigned long long input)
{
char* p = result;
while(input > 0)
{
*p++ = charset[input % CHARSET_LENGTH];
input /= CHARSET_LENGTH;
}
// null termination
*p = '\0';
// need to reverse the output
char* o = result;
while(o + 1 < p)
swap(*++o, *--p);
}
This is almost a textbook case of how not to do this. Concatenating strings in a loop is a bad idea, both because appending isn't particularly fast, and because you're constantly allocating memory.
Note: your question states that you're converting to base-62, but the code seems to have 63 symbols. Which are you trying to do?
Given a 64-bit integer, you can calculate that you won't need any more than 11 digits in the result, so using a static 12 character buffer will certainly help improve your speed. On the other hand, it's likely that your C++ library has a long-long equivalent to ultoa, which will be pretty optimal.
Edit: Here's something I whipped up. It allows you to specify any desired base as well:
std::string ullToString(unsigned long long v, int base = 64) {
assert(base < 65);
assert(base > 1);
static const char digits[]="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/";
const int max_length=65;
static char buffer[max_length];
buffer[max_length-1]=0;
char *d = buffer + max_length-1;
do {
d--;
int remainder = v % base;
v /= base;
*d = digits[remainder];
} while(v>0);
return d;
}
This only creates one std::string object, and doesn't move memory around unnecessarily. It currently doesn't zero-pad the output, but it's trivial to change it to do that to however many digits of output you want.
You don't need to copy input into num, because you pass it by value. You can also compute the length of charset in compiletime, there's no need to compute it in runtime every single time you call the function.
But these are very minor performance issues. I think the the most significant help you can gain is by avoiding the string concatenation in the loop. When you construct the key string pass the string constructor the length of your result string so that there is only one allocation for the string. Then in the loop when you concatenate into the string you will not re-allocate.
You can make things even slightly more efficient if you take the target string as a reference parameter or even as two iterators like the standard algorithms do. But that is arguably a step too far.
By the way, what if the value passed in for input is zero? You won't even enter the loop; shouldn't key then be "0"?
I see the value passed in for input can't be negative, but just so we note: the C remainder operator isn't a modulo operator.
Why not just use a base64 library? Is really important that 63 equals '11' and not a longer string?
size_t base64_encode(char* outbuffer, size_t maxoutbuflen, const char* inbuffer, size_t inbuflen);
std::string integerToKey(unsigned long long input) {
char buffer[14];
size_t len = base64_encode(buffer, sizeof buffer, (const char*)&input, sizeof input);
return std::string(buffer, len);
}
Yes, every string will end with an equal size. If you don't want it to, strip off the equal sign. (Just remember to add it back if you need to decode the number.)
Of course, my real question is why are you turning a fixed width 8byte value and not using it directly as your "key" instead of the variable length string value?
Footnote: I'm well aware of the endian issues with this. He didn't say what the key will be used for and so I assume it isn't being used in network communications between machines of disparate endian-ness.
If you could add two more symbols so that it is converting to base-64, your modulus and division operations would turn into a bit mask and shift. Much faster than a division.
If all you need is a short string key, converting to base-64 numbers would speed up things a lot, since div/mod 64 is very cheap (shift/mask).