872 lines · 30.8 KB · C++
RawDownload
////  SharedUtil.cpp//  libraries/shared/src////  Created by Stephen Birarda on 2/22/13.//  Copyright 2013 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "SharedUtil.h" #include <cassert>#include <cstdlib>#include <cstdio>#include <cstring>#include <cctype>#include <time.h>#include <mutex> #include <glm/glm.hpp>  #ifdef _WIN32#include <windows.h>#endif #ifdef Q_OS_WIN#include "CPUIdent.h"#include <Psapi.h>#endif  #ifdef __APPLE__#include <CoreFoundation/CoreFoundation.h>#endif #include <QtCore/QDebug>#include <QDateTime>#include <QElapsedTimer>#include <QProcess>#include <QSysInfo>#include <QThread> #include "NumericalConstants.h"#include "OctalCode.h"#include "SharedLogging.h" static qint64 usecTimestampNowAdjust = 0; // in usecvoid usecTimestampNowForceClockSkew(qint64 clockSkew) {    ::usecTimestampNowAdjust = clockSkew;} static qint64 TIME_REFERENCE = 0; // in usecstatic std::once_flag usecTimestampNowIsInitialized;static QElapsedTimer timestampTimer; quint64 usecTimestampNow(bool wantDebug) {    std::call_once(usecTimestampNowIsInitialized, [&] {        TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec        timestampTimer.start();    });        quint64 now;    quint64 nsecsElapsed = timestampTimer.nsecsElapsed();    quint64 usecsElapsed = nsecsElapsed / NSECS_PER_USEC;  // nsec to usec        // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it    // will begin to deviate from real time. We detect that here, and reset if necessary    quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch();    quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / USECS_PER_MSEC; // usecs to msecs    int possibleSkew = msecsEstimate - msecsCurrentTime;    const int TOLERANCE = 10 * MSECS_PER_SECOND; // up to 10 seconds of skew is tolerated    if (abs(possibleSkew) > TOLERANCE) {        // reset our TIME_REFERENCE and timer        TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec        timestampTimer.restart();        now = TIME_REFERENCE + ::usecTimestampNowAdjust;         if (wantDebug) {            qCDebug(shared) << "usecTimestampNow() - resetting QElapsedTimer. ";            qCDebug(shared) << "    msecsCurrentTime:" << msecsCurrentTime;            qCDebug(shared) << "       msecsEstimate:" << msecsEstimate;            qCDebug(shared) << "        possibleSkew:" << possibleSkew;            qCDebug(shared) << "           TOLERANCE:" << TOLERANCE;                        qCDebug(shared) << "        nsecsElapsed:" << nsecsElapsed;            qCDebug(shared) << "        usecsElapsed:" << usecsElapsed;             QDateTime currentLocalTime = QDateTime::currentDateTime();             quint64 msecsNow = now / 1000; // usecs to msecs            QDateTime nowAsString;            nowAsString.setMSecsSinceEpoch(msecsNow);             qCDebug(shared) << "                 now:" << now;            qCDebug(shared) << "            msecsNow:" << msecsNow;             qCDebug(shared) << "         nowAsString:" << nowAsString.toString("yyyy-MM-dd hh:mm:ss.zzz");            qCDebug(shared) << "    currentLocalTime:" << currentLocalTime.toString("yyyy-MM-dd hh:mm:ss.zzz");        }    } else {        now = TIME_REFERENCE + usecsElapsed + ::usecTimestampNowAdjust;    }     if (wantDebug) {        QDateTime currentLocalTime = QDateTime::currentDateTime();         quint64 msecsNow = now / 1000; // usecs to msecs        QDateTime nowAsString;        nowAsString.setMSecsSinceEpoch(msecsNow);         quint64 msecsTimeReference = TIME_REFERENCE / 1000; // usecs to msecs        QDateTime timeReferenceAsString;        timeReferenceAsString.setMSecsSinceEpoch(msecsTimeReference);         qCDebug(shared) << "usecTimestampNow() - details... ";        qCDebug(shared) << "           TIME_REFERENCE:" << TIME_REFERENCE;        qCDebug(shared) << "    timeReferenceAsString:" << timeReferenceAsString.toString("yyyy-MM-dd hh:mm:ss.zzz");        qCDebug(shared) << "   usecTimestampNowAdjust:" << usecTimestampNowAdjust;        qCDebug(shared) << "             nsecsElapsed:" << nsecsElapsed;        qCDebug(shared) << "             usecsElapsed:" << usecsElapsed;        qCDebug(shared) << "                      now:" << now;        qCDebug(shared) << "                 msecsNow:" << msecsNow;        qCDebug(shared) << "              nowAsString:" << nowAsString.toString("yyyy-MM-dd hh:mm:ss.zzz");        qCDebug(shared) << "         currentLocalTime:" << currentLocalTime.toString("yyyy-MM-dd hh:mm:ss.zzz");    }        return now;} float secTimestampNow() {    static const auto START_TIME = usecTimestampNow();    const auto nowUsecs = usecTimestampNow() - START_TIME;    const auto nowMsecs = nowUsecs / USECS_PER_MSEC;    return (float)nowMsecs / MSECS_PER_SECOND;} float randFloat() {    return (rand() % 10000)/10000.0f;} int randIntInRange (int min, int max) {    return min + (rand() % ((max + 1) - min));} float randFloatInRange (float min,float max) {    return min + ((rand() % 10000)/10000.0f * (max-min));} float randomSign() {    return randomBoolean() ? -1.0 : 1.0;} unsigned char randomColorValue(int miniumum) {    return miniumum + (rand() % (256 - miniumum));} bool randomBoolean() {    return rand() % 2;} bool shouldDo(float desiredInterval, float deltaTime) {    return randFloat() < deltaTime / desiredInterval;} void outputBufferBits(const unsigned char* buffer, int length, QDebug* continuedDebug) {    for (int i = 0; i < length; i++) {        outputBits(buffer[i], continuedDebug);    }} void outputBits(unsigned char byte, QDebug* continuedDebug) {    QDebug debug = qDebug().nospace();     if (continuedDebug) {        debug = *continuedDebug;        debug.nospace();    }     QString resultString;     if (isalnum(byte)) {        resultString.sprintf("[ %d (%c): ", byte, byte);    } else {        resultString.sprintf("[ %d (0x%x): ", byte, byte);    }    debug << qPrintable(resultString);        for (int i = 0; i < 8; i++) {        resultString.sprintf("%d", byte >> (7 - i) & 1);        debug << qPrintable(resultString);    }    debug << " ]";} int numberOfOnes(unsigned char byte) {     static const int nbits[256] = {        0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,        4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,        4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,        3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,        4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,        4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,        3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,        6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,        4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,        6,5,6,6,7,5,6,6,7,6,7,7,8    };     return nbits[(unsigned char) byte]; } bool oneAtBit(unsigned char byte, int bitIndex) {    return (byte >> (7 - bitIndex) & 1);} void setAtBit(unsigned char& byte, int bitIndex) {    byte |= (1 << (7 - bitIndex));} void clearAtBit(unsigned char& byte, int bitIndex) {    if (oneAtBit(byte, bitIndex)) {        byte -= (1 << (7 - bitIndex));    }} int  getSemiNibbleAt(unsigned char byte, int bitIndex) {    return (byte >> (6 - bitIndex) & 3); // semi-nibbles store 00, 01, 10, or 11} int getNthBit(unsigned char byte, int ordinal) {    const int ERROR_RESULT = -1;    const int MIN_ORDINAL = 1;    const int MAX_ORDINAL = 8;    if (ordinal < MIN_ORDINAL || ordinal > MAX_ORDINAL) {        return ERROR_RESULT;    }    int bitsSet = 0;    for (int bitIndex = 0; bitIndex < MAX_ORDINAL; bitIndex++) {        if (oneAtBit(byte, bitIndex)) {            bitsSet++;        }        if (bitsSet == ordinal) {            return bitIndex;        }    }    return ERROR_RESULT;} void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) {    //assert(value <= 3 && value >= 0);    byte |= ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11} bool isInEnvironment(const char* environment) {    char* environmentString = getenv("HIFI_ENVIRONMENT");    return (environmentString && strcmp(environmentString, environment) == 0);} //////////////////////////////////////////////////////////////////////////////////////////// Function:    getCmdOption()// Description: Handy little function to tell you if a command line flag and option was//              included while launching the application, and to get the option value//              immediately following the flag. For example if you ran://                      ./app -i filename.txt//              then you're using the "-i" flag to set the input file name.// Usage:       char * inputFilename = getCmdOption(argc, argv, "-i");// Complaints:  Brad :)const char* getCmdOption(int argc, const char * argv[],const char* option) {    // check each arg    for (int i=0; i < argc; i++) {        // if the arg matches the desired option        if (strcmp(option,argv[i])==0 && i+1 < argc) {            // then return the next option            return argv[i+1];        }    }    return NULL;} //////////////////////////////////////////////////////////////////////////////////////////// Function:    getCmdOption()// Description: Handy little function to tell you if a command line option flag was//              included while launching the application. Returns bool true/false// Usage:       bool wantDump   = cmdOptionExists(argc, argv, "-d");// Complaints:  Brad :) bool cmdOptionExists(int argc, const char * argv[],const char* option) {    // check each arg    for (int i=0; i < argc; i++) {        // if the arg matches the desired option        if (strcmp(option,argv[i])==0) {            // then return the next option            return true;        }    }    return false;} void sharedMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) {    fprintf(stdout, "%s", message.toLocal8Bit().constData());} unsigned char* pointToOctalCode(float x, float y, float z, float s) {    return pointToVoxel(x, y, z, s);} /// Given a universal point with location x,y,z this will return the voxel/// voxel code corresponding to the closest voxel which encloses a cube with/// lower corners at x,y,z, having side of length S./// The input values x,y,z range 0.0 <= v < 1.0/// IMPORTANT: The voxel is returned to you a buffer which you MUST delete when you are/// done with it.unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, unsigned char g, unsigned char b ) {     // special case for size 1, the root node    if (s >= 1.0f) {        unsigned char* voxelOut = new unsigned char;        *voxelOut = 0;        return voxelOut;    }     float xTest, yTest, zTest, sTest;    xTest = yTest = zTest = sTest = 0.5f;     // First determine the voxelSize that will properly encode a    // voxel of size S.    unsigned int voxelSizeInOctets = 1;    while (sTest > s) {        sTest /= 2.0f;        voxelSizeInOctets++;    }     auto voxelSizeInBytes = bytesRequiredForCodeLength(voxelSizeInOctets); // (voxelSizeInBits/8)+1;    auto voxelBufferSize = voxelSizeInBytes + sizeof(rgbColor); // 3 for color     // allocate our resulting buffer    unsigned char* voxelOut = new unsigned char[voxelBufferSize];     // first byte of buffer is always our size in octets    voxelOut[0]=voxelSizeInOctets;     sTest = 0.5f; // reset sTest so we can do this again.     unsigned char byte = 0; // we will be adding coding bits here    int bitInByteNDX = 0; // keep track of where we are in byte as we go    int byteNDX = 1; // keep track of where we are in buffer of bytes as we go    unsigned int octetsDone = 0;     // Now we actually fill out the voxel code    while (octetsDone < voxelSizeInOctets) {        if (x >= xTest) {            //<write 1 bit>            byte = (byte << 1) | true;            xTest += sTest/2.0f;        } else {            //<write 0 bit;>            byte = (byte << 1) | false;            xTest -= sTest/2.0f;        }        bitInByteNDX++;        // If we've reached the last bit of the byte, then we want to copy this byte        // into our buffer. And get ready to start on a new byte        if (bitInByteNDX == 8) {            voxelOut[byteNDX]=byte;            byteNDX++;            bitInByteNDX=0;            byte=0;        }         if (y >= yTest) {            //<write 1 bit>            byte = (byte << 1) | true;            yTest += sTest/2.0f;        } else {            //<write 0 bit;>            byte = (byte << 1) | false;            yTest -= sTest/2.0f;        }        bitInByteNDX++;        // If we've reached the last bit of the byte, then we want to copy this byte        // into our buffer. And get ready to start on a new byte        if (bitInByteNDX == 8) {            voxelOut[byteNDX]=byte;            byteNDX++;            bitInByteNDX=0;            byte=0;        }         if (z >= zTest) {            //<write 1 bit>            byte = (byte << 1) | true;            zTest += sTest/2.0f;        } else {            //<write 0 bit;>            byte = (byte << 1) | false;            zTest -= sTest/2.0f;        }        bitInByteNDX++;        // If we've reached the last bit of the byte, then we want to copy this byte        // into our buffer. And get ready to start on a new byte        if (bitInByteNDX == 8) {            voxelOut[byteNDX]=byte;            byteNDX++;            bitInByteNDX=0;            byte=0;        }         octetsDone++;        sTest /= 2.0f;    }     // If we've got here, and we didn't fill the last byte, we need to zero pad this    // byte before we copy it into our buffer.    if (bitInByteNDX > 0 && bitInByteNDX < 8) {        // Pad the last byte        while (bitInByteNDX < 8) {            byte = (byte << 1) | false;            bitInByteNDX++;        }         // Copy it into our output buffer        voxelOut[byteNDX]=byte;        byteNDX++;    }    // copy color data    voxelOut[byteNDX]=r;    voxelOut[byteNDX+1]=g;    voxelOut[byteNDX+2]=b;     return voxelOut;} void printVoxelCode(unsigned char* voxelCode) {    unsigned char octets = voxelCode[0];    unsigned int voxelSizeInBits = octets*3;    unsigned int voxelSizeInBytes = (voxelSizeInBits/8)+1;    unsigned int voxelSizeInOctets = (voxelSizeInBits/3);    unsigned int voxelBufferSize = voxelSizeInBytes+1+3; // 1 for size, 3 for color     qCDebug(shared, "octets=%d",octets);    qCDebug(shared, "voxelSizeInBits=%d",voxelSizeInBits);    qCDebug(shared, "voxelSizeInBytes=%d",voxelSizeInBytes);    qCDebug(shared, "voxelSizeInOctets=%d",voxelSizeInOctets);    qCDebug(shared, "voxelBufferSize=%d",voxelBufferSize);     for(unsigned int i=0; i < voxelBufferSize; i++) {        QDebug voxelBufferDebug = qDebug();        voxelBufferDebug << "i =" << i;        outputBits(voxelCode[i], &voxelBufferDebug);    }} #ifdef _WIN32void usleep(int waitTime) {    // Use QueryPerformanceCounter for least overhead    LARGE_INTEGER now; // ticks    QueryPerformanceCounter(&now);     static int64_t ticksPerSec = 0;    if (ticksPerSec == 0) {        LARGE_INTEGER frequency;        QueryPerformanceFrequency(&frequency);        ticksPerSec = frequency.QuadPart;    }     // order ops to avoid loss in precision    int64_t waitTicks = (ticksPerSec * waitTime) / USECS_PER_SECOND;    int64_t sleepTicks = now.QuadPart + waitTicks;     // Busy wait with sleep/yield where possible    while (true) {        QueryPerformanceCounter(&now);        if (now.QuadPart >= sleepTicks) {            break;        }         // Sleep if we have at least 1ms to spare        const int64_t MIN_SLEEP_USECS = 1000;        // msleep is allowed to overshoot, so give it a 100us berth        const int64_t MIN_SLEEP_USECS_BERTH = 100;        // order ops to avoid loss in precision        int64_t sleepFor = ((sleepTicks - now.QuadPart) * USECS_PER_SECOND) / ticksPerSec - MIN_SLEEP_USECS_BERTH;        if (sleepFor > MIN_SLEEP_USECS) {            Sleep((DWORD)(sleepFor / USECS_PER_MSEC));        // Yield otherwise        } else {            // Use Qt to delegate, as SwitchToThread is only supported starting with XP            QThread::yieldCurrentThread();        }    }}#endif // Inserts the value and key into three arrays sorted by the key array, the first array is the value,// the second array is a sorted key for the value, the third array is the index for the value in it original

Lines 1–500 of 872.