/commonLibrary/util.cc
C++ | 473 lines | 289 code | 96 blank | 88 comment | 72 complexity | 085fe1e3758230c0b2b7134e6c00f522 MD5 | raw file
- /*
- Copyright (c) 2010, Yahoo! Inc. All rights reserved.
- Redistribution and use of this software in source and binary forms, with or
- without modification, are permitted provided that the following conditions are
- met:
- * Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation and/or
- other materials provided with the distribution.
- * Neither the name of Yahoo! Inc. nor the names of its contributors may be used
- to endorse or promote products derived from this software without specific prior
- written permission of Yahoo! Inc.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include <iostream>
- #include <sstream>
- #include <algorithm>
- #include <string>
- #include <vector>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <fcntl.h>
- #include "util.h"
- ////////////////////////////////////////////////////////////////////////////////
- // Convert a long to a C string. Return a pointer to the
- // string. Caller must supply a buffer big enough to handle the
- // largest string. The returned point will point somewhere within the
- // buffer.
- ////////////////////////////////////////////////////////////////////////////////
- const char*
- util::ltoa(IA& iap, long ival)
- {
- bool negative = false;
- int strsize = sizeof(iap.buf);
- char* str = iap.buf;
- if (strsize < 2) return "0";
- --strsize;
- str += strsize;
- *str-- = '\0'; --strsize;
- if (ival < 0) {
- ival = - ival;
- negative = true;
- }
- do {
- *str-- = '0' + (ival % 10);
- ival /= 10;
- } while ((strsize > 0) && (ival > 0));
- if ((strsize > 0) && negative) *str-- = '-';
- return str+1;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Convert a long to an std::string. Inefficient, but easy.
- ////////////////////////////////////////////////////////////////////////////////
- std::string
- util::ltos(long i)
- {
- IA ia;
- const char* cp = ltoa(ia, i);
- std::string s(cp);
- return s;
- }
- //////////////////////////////////////////////////////////////////////
- // Convert seconds to WDXhYmZs for human consumption. Eg, 87304 will
- // results in an output of 1D2h3m4s.
- //////////////////////////////////////////////////////////////////////
- const std::string&
- util::durationToEnglish(int ss, std::string& result)
- {
- result.erase();
- int days = ss / (24 * 60 * 60);
- ss -= days * 24 * 60 * 60;
- int hh = ss / (60 * 60);
- ss -= hh * 60 * 60;
- int mm = ss / 60;
- ss -= mm * 60;
- if (days > 0) {
- result += ltos(days);
- result += "D";
- }
- if (hh > 0) {
- result += ltos(hh);
- result += "h";
- }
- if (mm > 0) {
- result += ltos(mm);
- result += "m";
- }
- if ((ss > 0) || result.empty()) {
- result += ltos(ss);
- result += "s";
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Convert a string like 1h to an int of seconds. Ditto for 2m, 3d and 4s.
- ////////////////////////////////////////////////////////////////////////////////
- extern int
- util::englishToDuration(std::string duration, std::string& em)
- {
- em.erase();
- int seconds = atoi(duration.c_str());
- std::string::size_type multiplier = duration.find_first_of("shmd");
- if (multiplier != std::string::npos) {
- switch (duration[multiplier]) {
- case 's': break;
- case 'm': seconds *= 60; break;
- case 'h': seconds *= 60 * 60; break;
- case 'd': seconds *= 60 * 60 * 24; break;
- default:
- em += "Error: Time multiplier of '";
- em += duration[multiplier];
- em += "' must be one of 's', 'm', 'h' or 'd'";
- return -1;
- }
- }
- return seconds;
- }
- //////////////////////////////////////////////////////////////////////
- // Convert an integer value to a rounded human K, M, G type
- // value. Accurate to within 10% due to displaying one decimal place.
- //////////////////////////////////////////////////////////////////////
- const std::string&
- util::intToEnglish(long val, std::string& result)
- {
- long point;
- result.erase();
- if (val < 1000) {
- result = ltos(val);
- return result;
- }
- point = val % 1000;
- val /= 1000;
- if (val < 1000) {
- result = ltos(val);
- if ((val < 10) && (point >= 100)) {
- result += ".";
- result += ltos(point / 100);
- }
- result += "K";
- return result;
- }
- point = val % 1000;
- val /= 1000;
- if (val < 1000) {
- result = ltos(val);
- if ((val < 10) && (point >= 100)) {
- result += ".";
- result += ltos(point / 100);
- }
- result += "M";
- return result;
- }
- point = val % 1000;
- val /= 1000;
- result = ltos(val);
- if ((val < 10) && (point >= 100)) {
- result += ".";
- result += ltos(point / 100);
- }
- result += "G";
- return result;
- }
- //////////////////////////////////////////////////////////////////////
- // Caculate a percentage and be aware of the fact that the supplied
- // numbers may be very close to the limits of long (thus denom * 100
- // may overflow).
- //////////////////////////////////////////////////////////////////////
- long
- util::outPercentage(long initDenom, long initNumer)
- {
- if (initNumer <= 0) return 0;
- if (initDenom == initNumer) return 100;
- if (initDenom == 0) return 0;
- long denom = initDenom;
- long numer = initNumer;
- // Scale depending on which end of the int range we're at
- if (numer < 10000) {
- denom *= 100;
- }
- else {
- numer /= 100;
- }
- denom /= numer;
- if (denom == 100) denom = 99; // Equality optimized earlier
- if (denom == 0) denom = 1; // Zeros caught earlier
- return denom;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Construct a string containing the message and the errno in decimal
- ////////////////////////////////////////////////////////////////////////////////
- const std::string&
- util::messageWithErrno(std::string& em, const char* message, const std::string& path)
- {
- return util::messageWithErrno(em, message, path.c_str(), 0);
- }
- const std::string&
- util::messageWithErrno(std::string& em, const char* message, const char* path, int additionalRC)
- {
- std::ostringstream os;
- if (message && *message) os << message;
- if (path && *path) {
- if (os.str().length()) os << ": ";
- os << "Path=" << path;
- }
- if (os.str().length()) os << ": ";
- os << "errno=" << errno << " " << strerror(errno);
- if (additionalRC != 0) os << "/" << additionalRC;
- em = os.str();
- return em;
- }
- //////////////////////////////////////////////////////////////////////
- // A = B - C (B must be greater than C for sensible results)
- //////////////////////////////////////////////////////////////////////
- extern void
- util::timevalDiff(timeval& A, const timeval& B, const timeval& C)
- {
- A = B;
- A.tv_sec -= C.tv_sec;
- A.tv_usec -= C.tv_usec;
- if (A.tv_usec < 0) {
- A.tv_sec--;
- A.tv_usec += util::MICROSECOND;
- }
- }
- //////////////////////////////////////////////////////////////////////
- // Return <0 if A < B, == 0 if A == B, > 0 if A > B
- //////////////////////////////////////////////////////////////////////
- extern int
- util::timevalCompare(const timeval& A, const timeval& B)
- {
- if (A.tv_sec < B.tv_sec) return -1;
- if (A.tv_sec > B.tv_sec) return 1;
- if (A.tv_usec < B.tv_usec) return -1;
- if (A.tv_usec > B.tv_usec) return 1;
- return 0;
- }
- //////////////////////////////////////////////////////////////////////
- // A += B
- //////////////////////////////////////////////////////////////////////
- extern void
- util::timevalAdd(timeval& A, const timeval& B)
- {
- A.tv_sec += B.tv_sec;
- A.tv_usec += B.tv_usec;
- while (A.tv_usec > util::MICROSECOND) {
- ++A.tv_sec;
- A.tv_usec -= util::MICROSECOND;
- }
- }
- //////////////////////////////////////////////////////////////////////
- extern void
- util::timevalNormalize(timeval& A)
- {
- if (A.tv_usec >= util::MICROSECOND) {
- A.tv_sec += A.tv_usec / util::MICROSECOND;
- A.tv_usec %= util::MICROSECOND;
- }
- }
- //////////////////////////////////////////////////////////////////////
- // Return A = B - C (B must be greater than C for sensible results)
- //////////////////////////////////////////////////////////////////////
- extern long
- util::timevalDiffMS(const timeval& B, const timeval& C)
- {
- timeval A;
- util::timevalDiff(A, B, C);
- return A.tv_sec * MILLISECOND + A.tv_usec / MILLISECOND;
- }
- //////////////////////////////////////////////////////////////////////
- // Return A = B - C in microseconds (B must be greater than C for
- // sensible results)
- //////////////////////////////////////////////////////////////////////
- extern long
- util::timevalDiffuS(const timeval& B, const timeval& C)
- {
- timeval A;
- util::timevalDiff(A, B, C);
- return A.tv_sec * MICROSECOND + A.tv_usec;
- }
- //////////////////////////////////////////////////////////////////////
- // Given a binary stream, return a hex string
- //////////////////////////////////////////////////////////////////////
- static char hexList[] = "0123456789ABCDEF";
- void
- util::hex(const unsigned char* source, unsigned int len, std::string& destination)
- {
- destination.erase();
- unsigned int ix;
- for (ix=0; ix < len; ++ix) {
- destination += hexList[(source[ix] & 0xF0) >> 4];
- destination += hexList[source[ix] & 0xF];
- }
- }
- //////////////////////////////////////////////////////////////////////
- // Undo util::hex. This routines assume a contiguous range from '0'
- // to '9' and 'A' to 'F'. ASCII, EBCDIC and FIELDDATA all meet that
- // constraint. If you're running this code on a machine with another
- // character set, all bets are off!
- //////////////////////////////////////////////////////////////////////
- int
- util::dehex(unsigned char* destination, unsigned int destinationSize,
- const unsigned char* source, unsigned int sourceLength)
- {
- int destinationLen = 0;
- while ((destinationSize > 0) && (sourceLength > 1)) {
- unsigned char h1 = *source++;
- unsigned char h2 = *source++;
- char c = 0;
- sourceLength -= 2; // It'd be cute to use --sourceLength--; Oh well.
- if ((h1 >= '0') && (h1 <= '9')) c = (h1 - '0') << 4;
- if ((h1 >= 'A') && (h1 <= 'F')) c = (h1 - 'A' + 10) << 4;
- if ((h2 >= '0') && (h2 <= '9')) c |= h2 - '0';
- if ((h2 >= 'A') && (h2 <= 'F')) c |= h2 - 'A' + 10;
- *destination++ = c;
- ++destinationLen;
- --destinationSize;
- }
- return destinationLen;
- }
- //////////////////////////////////////////////////////////////////////
- // Set the send and recv buffers as close to the supplied limit as
- // possible.
- //////////////////////////////////////////////////////////////////////
- void
- util::increaseOSBuffer(int fd, int maxRecvSize, int maxSendSize)
- {
- int sz;
- socklen_t msl = sizeof(sz);
- int res = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &sz, &msl);
- if ((res == -1) || (sz < maxRecvSize)) { // only change if it results in an increase
- while (maxRecvSize > sz) {
- msl = sizeof(maxRecvSize);
- res = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &maxRecvSize, msl);
- if (res == 0) break;
- maxRecvSize /= 2;
- }
- }
- msl = sizeof(sz);
- res = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sz, &msl);
- if ((res == -1) || (sz < maxSendSize)) { // only change if it results in an increase
- while (maxSendSize > sz) {
- msl = sizeof(maxSendSize);
- res = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &maxSendSize, msl);
- if (res == 0) break;
- maxSendSize /= 2;
- }
- }
- }
- int
- util::setNonBlocking(int fd)
- {
- return fcntl(fd, F_SETFL, O_NONBLOCK);
- }
- int
- util::setCloseOnExec(int fd)
- {
- return fcntl(fd, F_SETFD, FD_CLOEXEC);
- }