From b64af8dc8d65a28435031cbd61a5e0789fa26d98 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Mon, 15 Aug 2016 12:10:16 +0200 Subject: [PATCH 01/48] starting --- .../include/UDPStandardImplementation.h | 50 +++++++++++-------- .../src/UDPStandardImplementation.cpp | 17 ++++--- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index a3af9ab03..b4e85373d 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -477,6 +477,15 @@ private: * Class Members ********************************************************* *************************************************************************/ + /** Maximum Number of Writer Threads */ + +#ifdef DCOMPRESS + /**** most likely not used ***/ + const static int MAX_NUMBER_OF_WRITER_THREADS = 15; +#else + const static int MAX_NUMBER_OF_WRITER_THREADS = 2; +#endif + //**detector parameters*** /** Size of 1 Frame including headers */ int frameSize; @@ -513,7 +522,7 @@ private: #endif /** Complete File name */ - char completeFileName[MAX_STR_LENGTH]; + char completeFileName[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; /** Maximum Packets Per File **/ int maxPacketsPerFile; @@ -521,23 +530,24 @@ private: /** If file created successfully for all Writer Threads */ bool fileCreateSuccess; - char fileHeader[1000]; + char fileHeader[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; //***acquisition indices/count parameters*** /** Frame Number of First Frame of an entire Acquisition (including all scans) */ - uint64_t startAcquisitionIndex; + uint64_t startAcquisitionIndex[MAX_NUMBER_OF_LISTENING_THREADS]; /** Frame index at start of each real time acquisition (eg. for each scan) */ - uint64_t startFrameIndex; + uint64_t startFrameIndex[MAX_NUMBER_OF_LISTENING_THREADS]; /** Actual current frame index of each time acquisition (eg. for each scan) */ - uint64_t frameIndex; + uint64_t frameIndex[MAX_NUMBER_OF_WRITER_THREADS]; /** Current Frame Number */ - uint64_t currentFrameNumber; + uint64_t currentFrameNumber[MAX_NUMBER_OF_WRITER_THREADS]; + /** Previous Frame number from buffer to calculate loss */ int64_t previousFrameNumber; @@ -545,26 +555,27 @@ private: /** Last Frame Index Listened To */ int32_t lastFrameIndex; + /* Acquisition started */ - bool acqStarted; + bool acqStarted[MAX_NUMBER_OF_LISTENING_THREADS]; /* Measurement started */ - bool measurementStarted; + bool measurementStarted[MAX_NUMBER_OF_LISTENING_THREADS]; /** Total Frame Count listened to by listening threads */ int totalListeningFrameCount[MAX_NUMBER_OF_LISTENING_THREADS]; /** Pckets currently in current file, starts new file when it reaches max */ - uint32_t packetsInFile; + uint32_t packetsInFile[MAX_NUMBER_OF_WRITER_THREADS]; /** Number of Missing Packets per buffer*/ - uint32_t numMissingPackets; + uint32_t numMissingPackets[MAX_NUMBER_OF_WRITER_THREADS]; /** Total Number of Missing Packets in acquisition*/ - uint32_t numTotMissingPackets; + uint32_t numTotMissingPackets[MAX_NUMBER_OF_WRITER_THREADS]; /** Number of Missing Packets in file */ - uint32_t numTotMissingPacketsInFile; + uint32_t numTotMissingPacketsInFile[MAX_NUMBER_OF_WRITER_THREADS]; @@ -587,7 +598,7 @@ private: genericSocket* udpSocket[MAX_NUMBER_OF_LISTENING_THREADS]; /** File Descriptor */ - FILE *sfilefd; + FILE *sfilefd[MAX_NUMBER_OF_WRITER_THREADS]; /** Number of Jobs Per Buffer */ int numberofJobsPerBuffer; @@ -605,22 +616,22 @@ private: //***receiver to GUI parameters*** /** Current Frame copied for GUI */ - char* latestData; + char* latestData[MAX_NUMBER_OF_WRITER_THREADS]; /** If Data to be sent to GUI is ready */ - bool guiDataReady; + bool guiDataReady[MAX_NUMBER_OF_WRITER_THREADS]; /** Pointer to data to be sent to GUI */ - char* guiData; + char* guiData[MAX_NUMBER_OF_WRITER_THREADS]; /** Pointer to file name to be sent to GUI */ char guiFileName[MAX_STR_LENGTH]; /** Semaphore to synchronize Writer and GuiReader threads*/ - sem_t writerGuiSemaphore; + sem_t writerGuiSemaphore[MAX_NUMBER_OF_WRITER_THREADS]; /** counter for nth frame to gui */ - int frametoGuiCounter; + int frametoGuiCounter[MAX_NUMBER_OF_WRITER_THREADS]; @@ -653,9 +664,6 @@ private: //***writer thread parameters*** - /** Maximum Number of Writer Threads */ - const static int MAX_NUMBER_OF_WRITER_THREADS = 15; - /** Number of Writer Threads */ int numberofWriterThreads; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index d64692b65..c29177143 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -56,7 +56,8 @@ UDPStandardImplementation::UDPStandardImplementation(){ UDPStandardImplementation::~UDPStandardImplementation(){ FILE_LOG(logDEBUG) << __AT__ << " called"; - closeFile(); + for(int i=0;i Date: Mon, 15 Aug 2016 15:11:46 +0200 Subject: [PATCH 02/48] in between --- .../include/UDPStandardImplementation.h | 6 +- .../src/UDPStandardImplementation.cpp | 66 +++++++++++-------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index b4e85373d..d0a52ccb2 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -550,10 +550,10 @@ private: /** Previous Frame number from buffer to calculate loss */ - int64_t previousFrameNumber; + int64_t previousFrameNumber[MAX_NUMBER_OF_WRITER_THREADS]; /** Last Frame Index Listened To */ - int32_t lastFrameIndex; + int32_t lastFrameIndex[MAX_NUMBER_OF_WRITER_THREADS]; /* Acquisition started */ @@ -625,7 +625,7 @@ private: char* guiData[MAX_NUMBER_OF_WRITER_THREADS]; /** Pointer to file name to be sent to GUI */ - char guiFileName[MAX_STR_LENGTH]; + char guiFileName[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; /** Semaphore to synchronize Writer and GuiReader threads*/ sem_t writerGuiSemaphore[MAX_NUMBER_OF_WRITER_THREADS]; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index c29177143..b6e855d11 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -140,28 +140,31 @@ void UDPStandardImplementation::initializeMembers(){ } #endif for(int i=0; i Date: Mon, 15 Aug 2016 16:59:30 +0200 Subject: [PATCH 03/48] in between --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index b6e855d11..5b88c0cf7 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -791,7 +791,11 @@ int UDPStandardImplementation::setDetectorType(const detectorType d){ numberofJobsPerBuffer = -1; setupFifoStructure(); - //allocate for latest data (frame copy for gui) + //allocate for latest data (frame copy for gui), free variables + for(int i=0; i Date: Mon, 15 Aug 2016 17:32:27 +0200 Subject: [PATCH 04/48] in between --- slsReceiverSoftware/include/UDPStandardImplementation.h | 4 ++-- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index d0a52ccb2..26c01262d 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -557,10 +557,10 @@ private: /* Acquisition started */ - bool acqStarted[MAX_NUMBER_OF_LISTENING_THREADS]; + bool acqStarted; /* Measurement started */ - bool measurementStarted[MAX_NUMBER_OF_LISTENING_THREADS]; + bool measurementStarted; /** Total Frame Count listened to by listening threads */ int totalListeningFrameCount[MAX_NUMBER_OF_LISTENING_THREADS]; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 5b88c0cf7..645605409 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -150,10 +150,10 @@ void UDPStandardImplementation::initializeMembers(){ for(int i = 0; i < MAX_NUMBER_OF_LISTENING_THREADS; ++i){ startAcquisitionIndex[i] = 0; startFrameIndex[i] = 0; - acqStarted[i] = false; - measurementStarted[i] = false; totalListeningFrameCount[i] = 0; } + acqStarted = false; + measurementStarted = false; for(int i=0; i Date: Tue, 16 Aug 2016 11:44:20 +0200 Subject: [PATCH 05/48] in between --- .../src/UDPStandardImplementation.cpp | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 645605409..bc6c6ab28 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -813,9 +813,16 @@ int UDPStandardImplementation::setDetectorType(const detectorType d){ void UDPStandardImplementation::resetAcquisitionCount(){ FILE_LOG(logDEBUG) << __AT__ << " starting"; - totalPacketsCaught = 0; + for(int i=0;i Date: Tue, 16 Aug 2016 13:42:29 +0200 Subject: [PATCH 06/48] in between --- .../src/UDPStandardImplementation.cpp | 80 ++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index bc6c6ab28..3907f7834 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -833,47 +833,46 @@ int UDPStandardImplementation::startReceiver(char *c){ FILE_LOG(logINFO) << "Stopping Receiver"; + //reseting variables - //RESET - //reset measurement variables pthread_mutex_lock(&progressMutex); measurementStarted = false; pthread_mutex_unlock(&progressMutex); - - for(int i=0;i Date: Wed, 17 Aug 2016 09:33:59 +0200 Subject: [PATCH 07/48] in between --- .../include/UDPBaseImplementation.h | 4 +- slsReceiverSoftware/include/UDPInterface.h | 4 +- .../include/UDPStandardImplementation.h | 13 ++- .../src/UDPStandardImplementation.cpp | 109 ++++++++++-------- 4 files changed, 76 insertions(+), 54 deletions(-) diff --git a/slsReceiverSoftware/include/UDPBaseImplementation.h b/slsReceiverSoftware/include/UDPBaseImplementation.h index 568183e42..7562b0056 100644 --- a/slsReceiverSoftware/include/UDPBaseImplementation.h +++ b/slsReceiverSoftware/include/UDPBaseImplementation.h @@ -413,9 +413,9 @@ class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInter /** * Closes file / all files(if multiple files) - * @param i thread index (if multiple files used eg. root files) -1 for all threads + * @param i writer thread index */ - void closeFile(int i = -1); + void closeFile(int i = 0); //***callback functions*** diff --git a/slsReceiverSoftware/include/UDPInterface.h b/slsReceiverSoftware/include/UDPInterface.h index 9ad5a7e6a..2b3a3baba 100644 --- a/slsReceiverSoftware/include/UDPInterface.h +++ b/slsReceiverSoftware/include/UDPInterface.h @@ -470,9 +470,9 @@ class UDPInterface { /** * Closes file / all files(if multiple files) - * @param i thread index (if multiple files used eg. root files) -1 for all threads + * @param i writer thread index */ - virtual void closeFile(int i = -1) = 0; + virtual void closeFile(int i = 0) = 0; //***callback functions*** diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index 26c01262d..0f2c98bad 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -210,9 +210,9 @@ class UDPStandardImplementation: private virtual slsReceiverDefs, public UDPBase * Overridden method * Closes file / all files(data compression involves multiple files) * TCPIPInterface can also call this in case of illegal shutdown of receiver - * @param i thread index valid for datacompression using root files, -1 for all threads + * @param i writer thread index */ - void closeFile(int i = -1); + void closeFile(int i = 0); private: /************************************************************************* @@ -307,9 +307,10 @@ private: /** * Creates new file and reset some parameters + * @param ithread writer thread index * @return OK or FAIL */ - int createNewFile(); + int createNewFile(int ithread); /** * Creates new tree and file for compression @@ -428,10 +429,11 @@ private: /** * Calle by handleWithoutDataCompression * Creating headers Writing to file without compression + * @param ithread writer thread index * @param wbuffer is the address of buffer popped out of FIFO * @param numpackets is the number of packets */ - void writeFileWithoutCompression(char* wbuffer[],uint32_t numpackets); + void writeFileWithoutCompression(int ithread, char* wbuffer[],uint32_t numpackets); /** * Called by writeToFileWithoutCompression @@ -524,6 +526,9 @@ private: /** Complete File name */ char completeFileName[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; + /** File Prefix with detector index */ + char receiverFilePrefix[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; + /** Maximum Packets Per File **/ int maxPacketsPerFile; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 3907f7834..92e4e9636 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -10,6 +10,8 @@ #include "gotthardModuleData.h" #include "gotthardShortModuleData.h" +#include "fileIOStatic.h" + #include // exit() #include //set precision for printing parameters for create new file #include //map @@ -74,7 +76,8 @@ void UDPStandardImplementation::deleteMembers(){ FILE_LOG(logDEBUG) << "Info: Deleting member pointers"; shutDownUDPSockets(); - closeFile(); + for(int i=0;i config_map){ } -/***file parameters***/ int UDPStandardImplementation::setDataCompressionEnable(const bool b){ FILE_LOG(logDEBUG) << __AT__ << " starting"; @@ -958,12 +961,14 @@ void UDPStandardImplementation::stopReceiver(){ //wait until status is run_finished while(status == TRANSMITTING){ - sem_post(&writerGuiSemaphore); + for(int i=0; i < numberofWriterThreads; i++) + sem_post(&writerGuiSemaphore[i]); usleep(5000); } //semaphore destroy - sem_destroy(&writerGuiSemaphore); + for(int i=0; i < numberofWriterThreads; i++) + sem_destroy(&writerGuiSemaphore[i]); //change status pthread_mutex_lock(&statusMutex); @@ -982,8 +987,6 @@ void UDPStandardImplementation::stopReceiver(){ int UDPStandardImplementation::shutDownUDPSockets(){ FILE_LOG(logDEBUG) << __AT__ << " called"; - - for(int i=0;iShutDownSocket(); @@ -1033,8 +1036,6 @@ void UDPStandardImplementation::startReadout(){ } } - - //set status pthread_mutex_lock(&statusMutex); status = TRANSMITTING; @@ -1049,26 +1050,26 @@ void UDPStandardImplementation::startReadout(){ - -void UDPStandardImplementation::readFrame(char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame){ +/**make this better by asking all of it at once*/ +void UDPStandardImplementation::readFrame(int wThread, char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame){ FILE_LOG(logDEBUG) << __AT__ << " called"; //point to gui data, to let writer thread know that gui is back for data - if (guiData == NULL){ - guiData = latestData; + if (guiData[wThread] == NULL){ + guiData[wThread] = latestData[wThread]; #ifdef DEBUG4 cprintf(CYAN,"Info: gui data not null anymore - ready to get data\n"); #endif } //copy data and filename - strcpy(c,guiFileName); - startAcq = startAcquisitionIndex; - startFrame = startFrameIndex; + strcpy(c,guiFileName[wThread]); + startAcq = startAcquisitionIndex[wThread]; + startFrame = startFrameIndex[wThread]; //gui data not copied yet - if(!guiDataReady){ + if(!guiDataReady[wThread]){ #ifdef DEBUG4 cprintf(CYAN,"Info: gui data not ready\n"); #endif @@ -1080,8 +1081,8 @@ void UDPStandardImplementation::readFrame(char* c,char** raw, uint64_t &startAcq #ifdef DEBUG4 cprintf(CYAN,"Info: gui data ready\n"); #endif - *raw = guiData; - guiData = NULL; + *raw = guiData[wThread]; + guiData[wThread] = NULL; //for nth frame to gui, post semaphore so writer stops waiting if((FrameToGuiFrequency) && (writerThreadsMask)){ @@ -1089,7 +1090,7 @@ void UDPStandardImplementation::readFrame(char* c,char** raw, uint64_t &startAcq cprintf(CYAN,"Info: gonna post\n"); #endif //release after getting data - sem_post(&writerGuiSemaphore); + sem_post(&writerGuiSemaphore[wThread]); } #ifdef DEBUG4 cprintf(CYAN,"Info: done post\n"); @@ -1105,24 +1106,24 @@ void UDPStandardImplementation::closeFile(int i){ //normal if(!dataCompressionEnable){ - if(sfilefd){ + if(sfilefd[i]){ #ifdef DEBUG4 FILE_LOG(logDEBUG4) << "Going to close file: " << fileno(sfilefd)); #endif - fclose(sfilefd); - sfilefd = NULL; + fclose(sfilefd[i]); + sfilefd[i] = NULL; } } //compression else{ #if (defined(MYROOT1) && defined(ALLFILE_DEBUG)) || !defined(MYROOT1) - if(sfilefd){ + if(sfilefd[i]){ #ifdef DEBUG4 - FILE_LOG(logDEBUG4) << "sfield: " << (int)sfilefd; + FILE_LOG(logDEBUG4) << "sfield: " << (int)sfilefd[i]; #endif - fclose(sfilefd); - sfilefd = NULL; + fclose(sfilefd[i]); + sfilefd[i] = NULL; } #endif @@ -1293,7 +1294,9 @@ void UDPStandardImplementation::setThreadPriorities(){ rights = false; if(!rights){ - FILE_LOG(logWARNING) << "No root permission to prioritize threads."; + FILE_LOG(logWARNING) << "Unable to prioritize threads. Root privileges required for this option."; + }else{ + FILE_LOG(logINFO) << "Priorities set - TCP:50, Listening:99, Writing:90"; } } @@ -1375,7 +1378,8 @@ int UDPStandardImplementation::setupWriter(){ //creating first file //setting all value to 1 pthread_mutex_lock(&statusMutex); - for(int i=0; i= (uint32_t)maxPacketsPerFile) - createNewFile(); + createNewFile(0); pthread_mutex_unlock(&progressMutex); #endif From f17a2ba2b8e6e408830b7794259b8b42906d7e23 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Wed, 24 Aug 2016 11:54:15 +0200 Subject: [PATCH 08/48] in between --- .../include/UDPBaseImplementation.h | 4 +- slsReceiverSoftware/include/UDPInterface.h | 4 +- .../include/UDPStandardImplementation.h | 31 +- .../src/UDPBaseImplementation.cpp | 2 +- .../src/UDPStandardImplementation.cpp | 265 ++++++++++-------- .../src/slsReceiverTCPIPInterface.cpp | 5 + 6 files changed, 176 insertions(+), 135 deletions(-) diff --git a/slsReceiverSoftware/include/UDPBaseImplementation.h b/slsReceiverSoftware/include/UDPBaseImplementation.h index 7562b0056..f9c5e9273 100644 --- a/slsReceiverSoftware/include/UDPBaseImplementation.h +++ b/slsReceiverSoftware/include/UDPBaseImplementation.h @@ -413,9 +413,9 @@ class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInter /** * Closes file / all files(if multiple files) - * @param i writer thread index + * @param ithread writer thread index */ - void closeFile(int i = 0); + void closeFile(int ithread = 0); //***callback functions*** diff --git a/slsReceiverSoftware/include/UDPInterface.h b/slsReceiverSoftware/include/UDPInterface.h index 2b3a3baba..dba7acf52 100644 --- a/slsReceiverSoftware/include/UDPInterface.h +++ b/slsReceiverSoftware/include/UDPInterface.h @@ -470,9 +470,9 @@ class UDPInterface { /** * Closes file / all files(if multiple files) - * @param i writer thread index + * @param ithread writer thread index */ - virtual void closeFile(int i = 0) = 0; + virtual void closeFile(int ithread = 0) = 0; //***callback functions*** diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index 0f2c98bad..4be649189 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -199,20 +199,21 @@ class UDPStandardImplementation: private virtual slsReceiverDefs, public UDPBase /** * Overridden method * Get the buffer-current frame read by receiver + * @param ithread writer thread * @param c pointer to current file name * @param raw address of pointer, pointing to current frame to send to gui * @param startAcq start index of the acquisition * @param startFrame start index of the scan */ - void readFrame(char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame); + void readFrame(int ithread, char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame); /** * Overridden method * Closes file / all files(data compression involves multiple files) * TCPIPInterface can also call this in case of illegal shutdown of receiver - * @param i writer thread index + * @param ithread writer thread index */ - void closeFile(int i = 0); + void closeFile(int ithread = 0); private: /************************************************************************* @@ -444,16 +445,18 @@ private: /** * Updates the file header char aray, each time the corresp parameter is changed + * @param ithread writer thread index */ - void updateFileHeader(); + void updateFileHeader(int ithread); /** * Called by handleWithoutDataCompression and handleWithCompression after writing to file * Copy frames for GUI and updates appropriate parameters for frequency frames to gui * Uses semaphore for nth frame mode + * @param ithread writer thread index * @param buffer buffer to copy */ - void copyFrameToGui(char* buffer[]); + void copyFrameToGui(int ithread, char* buffer[]); void processWritingBuffer(int ithread); @@ -526,9 +529,6 @@ private: /** Complete File name */ char completeFileName[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; - /** File Prefix with detector index */ - char receiverFilePrefix[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; - /** Maximum Packets Per File **/ int maxPacketsPerFile; @@ -542,10 +542,10 @@ private: //***acquisition indices/count parameters*** /** Frame Number of First Frame of an entire Acquisition (including all scans) */ - uint64_t startAcquisitionIndex[MAX_NUMBER_OF_LISTENING_THREADS]; + uint64_t startAcquisitionIndex; /** Frame index at start of each real time acquisition (eg. for each scan) */ - uint64_t startFrameIndex[MAX_NUMBER_OF_LISTENING_THREADS]; + uint64_t startFrameIndex[MAX_NUMBER_OF_WRITER_THREADS]; /** Actual current frame index of each time acquisition (eg. for each scan) */ uint64_t frameIndex[MAX_NUMBER_OF_WRITER_THREADS]; @@ -564,8 +564,8 @@ private: /* Acquisition started */ bool acqStarted; - /* Measurement started */ - bool measurementStarted; + /* Measurement started - for each thread to get progress print outs*/ + bool measurementStarted[MAX_NUMBER_OF_LISTENING_THREADS]; /** Total Frame Count listened to by listening threads */ int totalListeningFrameCount[MAX_NUMBER_OF_LISTENING_THREADS]; @@ -582,6 +582,10 @@ private: /** Number of Missing Packets in file */ uint32_t numTotMissingPacketsInFile[MAX_NUMBER_OF_WRITER_THREADS]; + /** packets caught per thread */ + uint64_t packetsCaughtPerThread[MAX_NUMBER_OF_WRITER_THREADS]; + + @@ -720,6 +724,9 @@ private: /** Progress (currentFrameNumber) Mutex */ pthread_mutex_t progressMutex; + /** Progress (currentFrameNumber) Mutex */ + pthread_mutex_t udpSocketMutex[MAX_NUMBER_OF_LISTENING_THREADS]; + //***callback*** /** The action which decides what the user and default responsibilities to save data are * 0 raw data ready callback takes care of open,close,write file diff --git a/slsReceiverSoftware/src/UDPBaseImplementation.cpp b/slsReceiverSoftware/src/UDPBaseImplementation.cpp index a76cf0e9f..8168affa4 100644 --- a/slsReceiverSoftware/src/UDPBaseImplementation.cpp +++ b/slsReceiverSoftware/src/UDPBaseImplementation.cpp @@ -442,7 +442,7 @@ void UDPBaseImplementation::abort(){ FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; } -void UDPBaseImplementation::closeFile(int i){ +void UDPBaseImplementation::closeFile(int ithread){ FILE_LOG(logWARNING) << __AT__ << " doing nothing..."; FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; } diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 92e4e9636..96c81f465 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -38,6 +38,8 @@ UDPStandardImplementation::UDPStandardImplementation(){ pthread_mutex_init(&writeMutex,NULL); pthread_mutex_init(&dataReadyMutex,NULL); pthread_mutex_init(&progressMutex,NULL); + for(int i=0;iShutDownSocket(); FILE_LOG(logINFO) << "Shut down UDP Socket " << i; delete udpSocket[i]; udpSocket[i] = NULL; + pthread_mutex_unlock(&udpSocketMutex[i]); } } return OK; @@ -1051,25 +1052,25 @@ void UDPStandardImplementation::startReadout(){ /**make this better by asking all of it at once*/ -void UDPStandardImplementation::readFrame(int wThread, char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame){ +void UDPStandardImplementation::readFrame(int ithread, char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame){ FILE_LOG(logDEBUG) << __AT__ << " called"; //point to gui data, to let writer thread know that gui is back for data - if (guiData[wThread] == NULL){ - guiData[wThread] = latestData[wThread]; + if (guiData[ithread] == NULL){ + guiData[ithread] = latestData[ithread]; #ifdef DEBUG4 cprintf(CYAN,"Info: gui data not null anymore - ready to get data\n"); #endif } //copy data and filename - strcpy(c,guiFileName[wThread]); - startAcq = startAcquisitionIndex[wThread]; - startFrame = startFrameIndex[wThread]; + strcpy(c,guiFileName[ithread]); + startAcq = startAcquisitionIndex; + startFrame = startFrameIndex[ithread]; //gui data not copied yet - if(!guiDataReady[wThread]){ + if(!guiDataReady[ithread]){ #ifdef DEBUG4 cprintf(CYAN,"Info: gui data not ready\n"); #endif @@ -1081,8 +1082,8 @@ void UDPStandardImplementation::readFrame(int wThread, char* c,char** raw, uint6 #ifdef DEBUG4 cprintf(CYAN,"Info: gui data ready\n"); #endif - *raw = guiData[wThread]; - guiData[wThread] = NULL; + *raw = guiData[ithread]; + guiData[ithread] = NULL; //for nth frame to gui, post semaphore so writer stops waiting if((FrameToGuiFrequency) && (writerThreadsMask)){ @@ -1090,7 +1091,7 @@ void UDPStandardImplementation::readFrame(int wThread, char* c,char** raw, uint6 cprintf(CYAN,"Info: gonna post\n"); #endif //release after getting data - sem_post(&writerGuiSemaphore[wThread]); + sem_post(&writerGuiSemaphore[ithread]); } #ifdef DEBUG4 cprintf(CYAN,"Info: done post\n"); @@ -1101,53 +1102,53 @@ void UDPStandardImplementation::readFrame(int wThread, char* c,char** raw, uint6 -void UDPStandardImplementation::closeFile(int i){ - FILE_LOG(logDEBUG) << __AT__ << " called for " << i ; +void UDPStandardImplementation::closeFile(int ithread){ + FILE_LOG(logDEBUG) << __AT__ << " called for " << ithread ; //normal if(!dataCompressionEnable){ - if(sfilefd[i]){ + if(sfilefd[ithread]){ #ifdef DEBUG4 FILE_LOG(logDEBUG4) << "Going to close file: " << fileno(sfilefd)); #endif - fclose(sfilefd[i]); - sfilefd[i] = NULL; + fclose(sfilefd[ithread]); + sfilefd[ithread] = NULL; } } //compression else{ #if (defined(MYROOT1) && defined(ALLFILE_DEBUG)) || !defined(MYROOT1) - if(sfilefd[i]){ + if(sfilefd[0]){ #ifdef DEBUG4 FILE_LOG(logDEBUG4) << "sfield: " << (int)sfilefd[i]; #endif - fclose(sfilefd[i]); - sfilefd[i] = NULL; + fclose(sfilefd[0]); + sfilefd[0] = NULL; } #endif #ifdef MYROOT1 pthread_mutex_lock(&writeMutex); //write to file - if(myTree[i] && myFile[i]){ - myFile[i] = myTree[i]->GetCurrentFile(); + if(myTree[ithread] && myFile[ithread]){ + myFile[ithread] = myTree[ithread]->GetCurrentFile(); - if(myFile[i]->Write()) + if(myFile[ithread]->Write()) //->Write(tall->GetName(),TObject::kOverwrite); - cout << "Thread " << i <<": wrote frames to file" << endl; + cout << "Thread " << ithread <<": wrote frames to file" << endl; else - cout << "Thread " << i << ": could not write frames to file" << endl; + cout << "Thread " << ithread << ": could not write frames to file" << endl; }else - cout << "Thread " << i << ": could not write frames to file: No file or No Tree" << endl; + cout << "Thread " << ithread << ": could not write frames to file: No file or No Tree" << endl; //close file - if(myTree[i] && myFile[i]) - myFile[i] = myTree[i]->GetCurrentFile(); - if(myFile[i] != NULL) - myFile[i]->Close(); - myFile[i] = NULL; - myTree[i] = NULL; + if(myTree[ithread] && myFile[ithread]) + myFile[ithread] = myTree[ithread]->GetCurrentFile(); + if(myFile[ithread] != NULL) + myFile[ithread]->Close(); + myFile[ithread] = NULL; + myTree[ithread] = NULL; pthread_mutex_unlock(&writeMutex); #endif @@ -1363,7 +1364,8 @@ int UDPStandardImplementation::setupWriter(){ //acquisition start call back returns enable write cbAction = DO_EVERYTHING; if (startAcquisitionCallBack) - cbAction=startAcquisitionCallBack(filePath,fileName,(int)fileIndex,bufferSize,pStartAcquisition); + cbAction=startAcquisitionCallBack(filePath,fileName[0],(int)fileIndex,bufferSize,pStartAcquisition); + if(cbAction < DO_EVERYTHING){ FILE_LOG(logINFO) << "Call back activated. Data saving must be taken care of by user in call back."; @@ -1406,16 +1408,16 @@ int UDPStandardImplementation::createNewFile(int ithread){ FILE_LOG(logDEBUG) << __AT__ << " called"; int index = 0; - if(packetsCaught) + if(packetsCaughtPerThread[ithread]) index = frameIndex[ithread]; //create file name if(!frameIndexEnable) - sprintf(completeFileName[ithread], "%s/%s_%lld.raw", filePath,fileName,(long long int)fileIndex); + sprintf(completeFileName[ithread], "%s/%s_%lld.raw", filePath,fileName[ithread],(long long int)fileIndex); else if (myDetectorType == EIGER) - sprintf(completeFileName[ithread], "%s/%s_f%012lld_%lld.raw", filePath,fileName,(long long int)currentFrameNumber,(long long int)fileIndex); + sprintf(completeFileName[ithread], "%s/%s_f%012lld_%lld.raw", filePath,fileName[ithread],(long long int)currentFrameNumber[ithread],(long long int)fileIndex); else - sprintf(completeFileName[ithread], "%s/%s_f%012lld_%lld.raw", filePath,fileName,(long long int)(packetsCaught/packetsPerFrame),(long long int)fileIndex); + sprintf(completeFileName[ithread], "%s/%s_f%012lld_%lld.raw", filePath,fileName[ithread],(long long int)(packetsCaught[ithread]/packetsPerFrame),(long long int)fileIndex); #ifdef DEBUG4 FILE_LOG(logINFO) << completefileName; @@ -1425,54 +1427,56 @@ int UDPStandardImplementation::createNewFile(int ithread){ if(fileWriteEnable && cbAction > DO_NOTHING){ //close file pointers - if(sfilefd){ - fclose(sfilefd); - sfilefd = NULL; + if(sfilefd[ithread]){ + fclose(sfilefd[ithread]); + sfilefd[ithread] = NULL; } //create file if(!overwriteEnable){ - if (NULL == (sfilefd = fopen((const char *) (completeFileName), "wx"))){ - FILE_LOG(logERROR) << "Could not create/overwrite file" << completeFileName; + if (NULL == (sfilefd[ithread] = fopen((const char *) (completeFileName[ithread]), "wx"))){ + FILE_LOG(logERROR) << "Could not create/overwrite file" << completeFileName[ithread]; return FAIL; } - }else if (NULL == (sfilefd = fopen((const char *) (completeFileName), "w"))){ - FILE_LOG(logERROR) << "Could not create file" << completeFileName; + }else if (NULL == (sfilefd[ithread] = fopen((const char *) (completeFileName[ithread]), "w"))){ + FILE_LOG(logERROR) << "Could not create file" << completeFileName[ithread]; return FAIL; } //setting file buffer size to 16mb - setvbuf(sfilefd,NULL,_IOFBF,BUF_SIZE); + setvbuf(sfilefd[ithread],NULL,_IOFBF,BUF_SIZE); + //Print packet loss and filenames - if(!packetsCaught){ - previousFrameNumber = -1; - cout << "File: " << completeFileName << endl; + if(!packetsCaughtPerThread[ithread]){ + previousFrameNumber[ithread] = -1; + cout << "File: " << completeFileName[ithread] << endl; }else{ - if (previousFrameNumber == -1) - previousFrameNumber = startFrameIndex-1; + //Assumption for startFrameindex usign ithread: datacompression never enters here and therefore is always same number of listening and writing threads to use ithread + if (previousFrameNumber[ithread] == -1) + previousFrameNumber[ithread] = startFrameIndex[ithread]-1; - cout << completeFileName + cout << completeFileName[ithread] << "\tPacket Loss: " << setw(4)<initEventTree(temp, &iframe); //resets the pedestalSubtraction array and the commonModeSubtraction singlePhotonDetectorObject[ithread]->newDataSet(); @@ -1589,19 +1593,14 @@ void UDPStandardImplementation::startListening(){ rc = prepareAndListenBuffer(ithread, listenSize, carryonBufferSize, tempBuffer); //start indices for each start of scan/acquisition - if((!measurementStarted) && (rc > 0)){ - pthread_mutex_lock(&progressMutex); - if(!measurementStarted) - startFrameIndices(ithread); - pthread_mutex_unlock(&progressMutex); - } - - + if((!measurementStarted) && (rc > 0)) + startFrameIndices(ithread); //problem in receiving or end of acquisition if (status == TRANSMITTING){ stopListening(ithread,rc); continue; } + //write packet count to buffer if(myDetectorType == EIGER) (*((uint32_t*)(buffer[ithread]))) = 1; @@ -1653,10 +1652,14 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int lSize, in if(cSize) memcpy(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS, temp, cSize); + pthread_mutex_lock(&udpSocketMutex[ithread]); int receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, lSize + cSize); - - //throw away packets that is not one packet size, need to check status if socket is shut down - while(status != TRANSMITTING && myDetectorType == EIGER && receivedSize != onePacketSize) { + //throw away packets that is not one packet size + while(myDetectorType == EIGER && receivedSize != onePacketSize) { + //need to check status if socket is shut down + if(status == TRANSMITTING) + break; + //print if(receivedSize != EIGER_HEADER_LENGTH){ cprintf(RED,"Listening_Thread %d: Listened to a weird packet size %d\n",ithread, receivedSize); } @@ -1664,8 +1667,11 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int lSize, in else cprintf(BLUE,"Listening_Thread %d: Listened to a header packet\n",ithread); #endif + //listen again receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS); } + pthread_mutex_unlock(&udpSocketMutex[ithread]); + totalListeningFrameCount[ithread] += (receivedSize/onePacketSize); #ifdef MANUALDEBUG @@ -1710,18 +1716,18 @@ void UDPStandardImplementation::startFrameIndices(int ithread){ jfrau_packet_header_t* header=0; switch(myDetectorType){ case EIGER: - startFrameIndex = 0; //frame number always resets + startFrameIndex[ithread] = 0; //frame number always resets break; case JUNGFRAU: header = (jfrau_packet_header_t*)(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS); - startFrameIndex = (*( (uint32_t*) header->frameNumber))&0xffffff; + startFrameIndex[ithread] = (*( (uint32_t*) header->frameNumber))&0xffffff; break; default: if(shortFrameEnable < 0){ - startFrameIndex = (((((uint32_t)(*((uint32_t*)(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS))))+1) + startFrameIndex[ithread] = (((((uint32_t)(*((uint32_t*)(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS))))+1) & (frameIndexMask)) >> frameIndexOffset); }else{ - startFrameIndex = ((((uint32_t)(*((uint32_t*)(buffer[ithread]+HEADER_SIZE_NUM_TOT_PACKETS)))) + startFrameIndex[ithread] = ((((uint32_t)(*((uint32_t*)(buffer[ithread]+HEADER_SIZE_NUM_TOT_PACKETS)))) & (frameIndexMask)) >> frameIndexOffset); } break; @@ -1729,14 +1735,16 @@ void UDPStandardImplementation::startFrameIndices(int ithread){ //start of entire acquisition if(!acqStarted){ - startAcquisitionIndex = startFrameIndex; + pthread_mutex_lock(&progressMutex); + startAcquisitionIndex = startFrameIndex[ithread]; acqStarted = true; + pthread_mutex_unlock(&progressMutex); cprintf(BLUE,"Listening_Thread %d: startAcquisitionIndex:%lld\n",ithread,(long long int)startAcquisitionIndex); } //set start of scan/real time measurement - cprintf(BLUE,"Listening_Thread %d: startFrameIndex: %lld\n", ithread,(long long int)startFrameIndex); - measurementStarted = true; + cprintf(BLUE,"Listening_Thread %d: startFrameIndex: %lld\n", ithread,(long long int)startFrameIndex[ithread]); + measurementStarted[ithread] = true; } @@ -2215,7 +2223,7 @@ void UDPStandardImplementation::processWritingBufferPacketByPacket(int ithread){ threadFrameNumber[i] = (uint32_t)(*( (uint64_t*) packetBuffer_footer)); //last frame read out lastFrameIndex = threadFrameNumber[i]; - threadFrameNumber[i] += (startFrameIndex - 1); + threadFrameNumber[i] += (startFrameIndex[ithread] - 1); //packet number currentPacketNumber[i] = *( (uint16_t*) packetBuffer_footer->packetNumber); @@ -2371,7 +2379,7 @@ void UDPStandardImplementation::processWritingBufferPacketByPacket(int ithread){ //ensuring last packet got is not of some other future frame but of the current one eiger_packet_footer_t* wbuf_footer1 = (eiger_packet_footer_t*)(packetBuffer[i] + footerOffset + HEADER_SIZE_NUM_TOT_PACKETS); - uint64_t packfnum = (((uint32_t)(*( (uint64_t*) wbuf_footer1)))+(startFrameIndex - 1)); + uint64_t packfnum = (((uint32_t)(*( (uint64_t*) wbuf_footer1)))+(startFrameIndex[ithread] - 1)); //to reset to get new frame: not dummy and the last packet if((numPackets[i] != dummyPacketValue) && (currentPacketNumber[i] == LAST_PACKET_VALUE) && (packfnum == currentFrameNumber) ) @@ -2460,13 +2468,27 @@ void UDPStandardImplementation::waitWritingBufferForNextAcquisition(int ithread) if((1< 1) pthread_mutex_lock(&writeMutex); packetsInFile += numpackets; + packetsCaughtPerThread[ithread] += (numpackets - numMissingPackets); + pthread_mutex_lock(&progressMutex); packetsCaught += (numpackets - numMissingPackets); + pthread_mutex_unlock(&progressMutex); totalPacketsCaught += (numpackets - numMissingPackets); numMissingPackets = 0; if(numberofWriterThreads > 1) pthread_mutex_unlock(&writeMutex); @@ -2906,7 +2934,7 @@ void UDPStandardImplementation::createHeaders(char* wbuffer[]){ } -void UDPStandardImplementation::updateFileHeader(){ +void UDPStandardImplementation::updateFileHeader(int ithread){ int xpix=-1,ypix=-1; //create detector specific packet header @@ -2934,10 +2962,10 @@ void UDPStandardImplementation::updateFileHeader(){ //update file header time_t t = time(0); - int length = sizeof(fileHeader); - while((unsigned int)length!=strlen(fileHeader)){ - length = strlen(fileHeader); - sprintf(fileHeader,"\nHeader\t\t %d bytes\n" + int length = sizeof(fileHeader[ithread]); + while((unsigned int)length!=strlen(fileHeader[ithread])){ + length = strlen(fileHeader[ithread]); + sprintf(fileHeader[ithread],"\nHeader\t\t %d bytes\n" "Dynamic Range\t %d\n" "Packet\t\t %d bytes\n" "x\t\t %d pixels\n" @@ -3037,7 +3065,7 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer pthread_mutex_unlock(&progressMutex); //set indices acquisitionIndex = currentFrameNumber - startAcquisitionIndex; - frameIndex = currentFrameNumber - startFrameIndex; + frameIndex = currentFrameNumber - startFrameIndex[0]; //variable definitions @@ -3123,6 +3151,7 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer #ifndef ALLFILE pthread_mutex_lock(&progressMutex); packetsInFile += packetsPerFrame; + packetsCaughtPerThread[0] += packetsPerFrame; packetsCaught += packetsPerFrame; totalPacketsCaught += packetsPerFrame; if(packetsInFile >= (uint32_t)maxPacketsPerFile) diff --git a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp index 1e2b5c49e..a07c4c284 100644 --- a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp +++ b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp @@ -370,6 +370,7 @@ int slsReceiverTCPIPInterface::set_detector_type(){ } if(ret != FAIL){ #ifndef REST + if(receiverBase) delete receiverBase; receiverBase = UDPInterface::create("standard"); if(startAcquisitionCallBack) receiverBase->registerCallBackStartAcquisition(startAcquisitionCallBack,pStartAcquisition); @@ -998,6 +999,10 @@ int slsReceiverTCPIPInterface::reset_frames_caught(){ strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING){ + strcpy(mess,"Cannot reset frames caught while status is running\n"); + ret=FAIL; + } else receiverBase->resetAcquisitionCount(); } From e9b7a11cf6ff3f0261ffa04818128b6110cbe655 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Wed, 24 Aug 2016 16:23:43 +0200 Subject: [PATCH 09/48] in between --- .../include/UDPStandardImplementation.h | 21 +- .../src/UDPStandardImplementation.cpp | 245 ++++++++---------- 2 files changed, 122 insertions(+), 144 deletions(-) diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index 4be649189..587b88dbd 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -348,12 +348,11 @@ private: * Also copies carryovers from previous frame in front of buffer (gotthard and moench) * For eiger, it ignores packets less than onePacketSize * @param ithread listening thread index - * @param lSize number of bytes to listen to * @param cSize number of bytes carried on from previous buffer * @param temp temporary storage of previous buffer * @return the number of bytes actually received */ - int prepareAndListenBuffer(int ithread, int lSize, int cSize, char* temp); + int prepareAndListenBuffer(int ithread, int cSize, char* temp); /** * Called by startListening @@ -380,9 +379,10 @@ private: * @param ithread listening thread index * @param cSize number of bytes carried over to the next buffer to reunite with split frame * @param temp temporary buffer to store the split frame + * @param rc number of bytes received * @return packet count */ - uint32_t processListeningBuffer(int ithread, int cSize,char* temp); + uint32_t processListeningBuffer(int ithread, int &cSize,char* temp, int rc); /** * Thread started which writes packets to file. @@ -415,7 +415,7 @@ private: * @param ithread writing thread index * @param wbuffer writing buffer popped out from FIFO */ - void stopWriting(int ithread, char* wbuffer[]); + void stopWriting(int ithread, char* wbuffer); /** * Called by processWritingBuffer and processWritingBufferPacketByPacket @@ -425,7 +425,7 @@ private: * @param wbuffer writing buffer popped out from FIFO * @param npackets number of packets */ - void handleWithoutDataCompression(int ithread, char* wbuffer[],uint32_t npackets); + void handleWithoutDataCompression(int ithread, char* wbuffer,uint32_t npackets); /** * Calle by handleWithoutDataCompression @@ -492,9 +492,6 @@ private: #endif //**detector parameters*** - /** Size of 1 Frame including headers */ - int frameSize; - /** Size of 1 buffer processed at a time */ int bufferSize; @@ -529,8 +526,8 @@ private: /** Complete File name */ char completeFileName[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; - /** Maximum Packets Per File **/ - int maxPacketsPerFile; + /** Maximum Frames Per File **/ + int maxFramesPerFile; /** If file created successfully for all Writer Threads */ bool fileCreateSuccess; @@ -545,7 +542,7 @@ private: uint64_t startAcquisitionIndex; /** Frame index at start of each real time acquisition (eg. for each scan) */ - uint64_t startFrameIndex[MAX_NUMBER_OF_WRITER_THREADS]; + uint64_t startFrameIndex; /** Actual current frame index of each time acquisition (eg. for each scan) */ uint64_t frameIndex[MAX_NUMBER_OF_WRITER_THREADS]; @@ -577,7 +574,7 @@ private: uint32_t numMissingPackets[MAX_NUMBER_OF_WRITER_THREADS]; /** Total Number of Missing Packets in acquisition*/ - uint32_t numTotMissingPackets[MAX_NUMBER_OF_WRITER_THREADS]; + uint32_t numTotMissingPackets; /** Number of Missing Packets in file */ uint32_t numTotMissingPacketsInFile[MAX_NUMBER_OF_WRITER_THREADS]; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 96c81f465..62da115d3 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -128,7 +128,6 @@ void UDPStandardImplementation::initializeMembers(){ FILE_LOG(logDEBUG) << "Info: Initializing members"; //***detector parameters*** - frameSize = 0; bufferSize = 0; onePacketSize = 0; oneDataSize = 0; @@ -149,18 +148,18 @@ void UDPStandardImplementation::initializeMembers(){ strcpy(fileHeader[i],""); sfilefd[i] = NULL; } - maxPacketsPerFile = 0; + maxFramesPerFile = 0; fileCreateSuccess = false; //***acquisition indices parameters*** startAcquisitionIndex = 0; acqStarted = false; + startFrameIndex = 0; for(int i = 0; i < MAX_NUMBER_OF_LISTENING_THREADS; ++i){ - startFrameIndex[i] = 0; measurementStarted[i] = false; totalListeningFrameCount[i] = 0; } - + numTotMissingPackets = 0; for(int i=0; i 1 numberofJobsPerBuffer if(fifoSize % numberofJobsPerBuffer) @@ -338,10 +333,8 @@ int UDPStandardImplementation::setupFifoStructure(){ fifoFree[i]->pop(buffer[i]); //cprintf(BLUE,"FifoFree[%d]: value:%d, pop 0x%x\n",i,fifoFree[i]->getSemValue(),(void*)(buffer[i])); } -#ifdef DEBUG5 - cprintf(BLUE,"Info: %d fifostructure popped from fifofree %p\n", i, (void*)(buffer[i])); -#endif delete fifoFree[i]; + fifoFree[i] = NULL; } if(fifo[i]){ while(!fifo[i]->isEmpty()){ @@ -349,8 +342,12 @@ int UDPStandardImplementation::setupFifoStructure(){ //cprintf(CYAN,"Fifo[%d]: value:%d, pop 0x%x\n",i,fifo[i]->getSemValue(),(void*)(buffer[i])); } delete fifo[i]; + fifo[i] = NULL; + } + if(mem0[i]){ + free(mem0[i]); + mem0[i] = NULL; } - if(mem0[i]) free(mem0[i]); //creating fifoFree[i] = new CircularFifo(fifoSize); @@ -469,22 +466,20 @@ void UDPStandardImplementation::setShortFrameEnable(const int i){ shortFrameEnable = i; if(shortFrameEnable!=-1){ - frameSize = GOTTHARD_SHORT_BUFFER_SIZE; bufferSize = GOTTHARD_SHORT_BUFFER_SIZE; onePacketSize = GOTTHARD_SHORT_BUFFER_SIZE; oneDataSize = GOTTHARD_SHORT_DATABYTES; - maxPacketsPerFile = SHORT_MAX_FRAMES_PER_FILE * GOTTHARD_SHORT_PACKETS_PER_FRAME; + maxFramesPerFile = SHORT_MAX_FRAMES_PER_FILE; packetsPerFrame = GOTTHARD_SHORT_PACKETS_PER_FRAME; frameIndexMask = GOTTHARD_SHORT_FRAME_INDEX_MASK; frameIndexOffset = GOTTHARD_SHORT_FRAME_INDEX_OFFSET; packetIndexMask = GOTTHARD_SHORT_PACKET_INDEX_MASK; }else{ - frameSize = GOTTHARD_BUFFER_SIZE; bufferSize = GOTTHARD_BUFFER_SIZE; onePacketSize = GOTTHARD_ONE_PACKET_SIZE; oneDataSize = GOTTHARD_ONE_DATA_SIZE; - maxPacketsPerFile = MAX_FRAMES_PER_FILE * GOTTHARD_PACKETS_PER_FRAME; + maxFramesPerFile = MAX_FRAMES_PER_FILE; packetsPerFrame = GOTTHARD_PACKETS_PER_FRAME; frameIndexMask = GOTTHARD_FRAME_INDEX_MASK; frameIndexOffset = GOTTHARD_FRAME_INDEX_OFFSET; @@ -517,9 +512,8 @@ int UDPStandardImplementation::setAcquisitionPeriod(const uint64_t i){ FILE_LOG(logDEBUG) << __AT__ << " called"; acquisitionPeriod = i; - if((myDetectorType == GOTTHARD) && (myDetectorType == MOENCH)) - if(setupFifoStructure() == FAIL) - return FAIL; + if(setupFifoStructure() == FAIL) + return FAIL; FILE_LOG(logINFO) << "Acquisition Period: " << (double)acquisitionPeriod/(1E9) << "s"; @@ -531,9 +525,8 @@ int UDPStandardImplementation::setNumberOfFrames(const uint64_t i){ FILE_LOG(logDEBUG) << __AT__ << " called"; numberOfFrames = i; - if((myDetectorType == GOTTHARD) && (myDetectorType == MOENCH)) - if(setupFifoStructure() == FAIL) - return FAIL; + if(setupFifoStructure() == FAIL) + return FAIL; FILE_LOG(logINFO) << "Number of Frames:" << numberOfFrames; @@ -551,12 +544,11 @@ int UDPStandardImplementation::setDynamicRange(const uint32_t i){ if(myDetectorType == EIGER){ //set parameters depending on new dynamic range. - packetsPerFrame = (tengigaEnable ? EIGER_TEN_GIGA_CONSTANT : EIGER_ONE_GIGA_CONSTANT) - * dynamicRange * EIGER_MAX_PORTS; - frameSize = onePacketSize * packetsPerFrame; - maxPacketsPerFile = EIGER_MAX_FRAMES_PER_FILE * packetsPerFrame; + packetsPerFrame = (tengigaEnable ? EIGER_TEN_GIGA_CONSTANT : EIGER_ONE_GIGA_CONSTANT) * dynamicRange; + bufferSize = onePacketSize * packetsPerFrame; - updateFileHeader(); + for(int i=0; i 0)) @@ -1603,10 +1589,10 @@ void UDPStandardImplementation::startListening(){ //write packet count to buffer if(myDetectorType == EIGER) - (*((uint32_t*)(buffer[ithread]))) = 1; - //handling split frames and writing packet Count to buffer - else - (*((uint32_t*)(buffer[ithread]))) = processListeningBuffer(ithread, carryonBufferSize, tempBuffer); + (*((uint32_t*)(buffer[ithread]))) = rc/onePacketSize; + + if(dataCompressionEnable) + (*((uint32_t*)(buffer[ithread]))) = processListeningBuffer(ithread, carryonBufferSize, tempBuffer, rc); //push buffer to FIFO @@ -1645,24 +1631,17 @@ void UDPStandardImplementation::startListening(){ -int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int lSize, int cSize, char* temp){ +int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, char* temp){ FILE_LOG(logDEBUG) << __AT__ << " called"; - //listen to UDP packets - if(cSize) - memcpy(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS, temp, cSize); + //carry over from previous buffer + if(cSize) memcpy(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS, temp, cSize); - pthread_mutex_lock(&udpSocketMutex[ithread]); - int receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, lSize + cSize); + int receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); //throw away packets that is not one packet size while(myDetectorType == EIGER && receivedSize != onePacketSize) { - //need to check status if socket is shut down - if(status == TRANSMITTING) - break; - //print - if(receivedSize != EIGER_HEADER_LENGTH){ + if(receivedSize != EIGER_HEADER_LENGTH) cprintf(RED,"Listening_Thread %d: Listened to a weird packet size %d\n",ithread, receivedSize); - } #ifdef DEBUG else cprintf(BLUE,"Listening_Thread %d: Listened to a header packet\n",ithread); @@ -1670,7 +1649,6 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int lSize, in //listen again receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS); } - pthread_mutex_unlock(&udpSocketMutex[ithread]); totalListeningFrameCount[ithread] += (receivedSize/onePacketSize); @@ -1696,8 +1674,6 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int lSize, in } } #endif - - #ifdef DEBUG cprintf(BLUE, "Listening_Thread %d : Received bytes: %d. Expected bytes: %d\n", ithread, receivedSize, bufferSize * numberofJobsPerBuffer-cSize); #endif @@ -1716,18 +1692,18 @@ void UDPStandardImplementation::startFrameIndices(int ithread){ jfrau_packet_header_t* header=0; switch(myDetectorType){ case EIGER: - startFrameIndex[ithread] = 0; //frame number always resets + startFrameIndex = 0; //frame number always resets break; case JUNGFRAU: header = (jfrau_packet_header_t*)(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS); - startFrameIndex[ithread] = (*( (uint32_t*) header->frameNumber))&0xffffff; + startFrameIndex = (*( (uint32_t*) header->frameNumber))&0xffffff; break; default: if(shortFrameEnable < 0){ - startFrameIndex[ithread] = (((((uint32_t)(*((uint32_t*)(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS))))+1) + startFrameIndex = (((((uint32_t)(*((uint32_t*)(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS))))+1) & (frameIndexMask)) >> frameIndexOffset); }else{ - startFrameIndex[ithread] = ((((uint32_t)(*((uint32_t*)(buffer[ithread]+HEADER_SIZE_NUM_TOT_PACKETS)))) + startFrameIndex = ((((uint32_t)(*((uint32_t*)(buffer[ithread]+HEADER_SIZE_NUM_TOT_PACKETS)))) & (frameIndexMask)) >> frameIndexOffset); } break; @@ -1736,14 +1712,14 @@ void UDPStandardImplementation::startFrameIndices(int ithread){ //start of entire acquisition if(!acqStarted){ pthread_mutex_lock(&progressMutex); - startAcquisitionIndex = startFrameIndex[ithread]; + startAcquisitionIndex = startFrameIndex; acqStarted = true; pthread_mutex_unlock(&progressMutex); cprintf(BLUE,"Listening_Thread %d: startAcquisitionIndex:%lld\n",ithread,(long long int)startAcquisitionIndex); } //set start of scan/real time measurement - cprintf(BLUE,"Listening_Thread %d: startFrameIndex: %lld\n", ithread,(long long int)startFrameIndex[ithread]); + cprintf(BLUE,"Listening_Thread %d: startFrameIndex: %lld\n", ithread,(long long int)startFrameIndex); measurementStarted[ithread] = true; } @@ -1863,13 +1839,13 @@ void UDPStandardImplementation::stopListening(int ithread, int numbytes){ -uint32_t UDPStandardImplementation::processListeningBuffer(int ithread, int cSize, char* temp){ +uint32_t UDPStandardImplementation::processListeningBuffer(int ithread, int &cSize, char* temp, int rc){ FILE_LOG(logDEBUG) << __AT__ << " called"; int lastPacketOffset; //the offset of the last packet uint32_t lastFrameHeader; //frame number of last packet in buffer uint64_t lastFrameHeader64; //frame number of last packet in buffer - uint32_t packetCount = (packetsPerFrame/numberofListeningThreads) * numberofJobsPerBuffer; //packets received + uint32_t packetCount = rc;//(packetsPerFrame/numberofListeningThreads) * numberofJobsPerBuffer; //packets received cSize = 0; //reset size jfrau_packet_header_t* header; @@ -2012,9 +1988,10 @@ void UDPStandardImplementation::processWritingBuffer(int ithread){ FILE_LOG(logDEBUG) << __AT__ << " called"; //variable definitions - char* wbuf[numberofListeningThreads]; //buffer popped from FIFO - sfilefd = NULL; //file pointer - uint64_t nf; //for compression, number of frames + char* wbuf; //buffer popped from FIFO + sfilefd[ithread] = NULL; //file pointer + uint64_t nf; //for compression, number of frames + int listenfifoIndex = ithread; /* outer loop - loops once for each acquisition */ @@ -2023,31 +2000,33 @@ void UDPStandardImplementation::processWritingBuffer(int ithread){ //--reset parameters before acquisition nf = 0; - guiData = latestData; //so that the first frame is always copied + guiData[ithread] = latestData[ithread]; //so that the first frame is always copied + if(dataCompressionEnable) + listenfifoIndex = 0; //compression has only one listening thread /* inner loop - loop for each buffer */ //until mask unset (udp sockets shut down by client) while((1 << ithread) & writerThreadsMask){ //pop - fifo[0]->pop(wbuf[0]); + fifo[listenfifoIndex]->pop(wbuf); #ifdef EVERYFIFODEBUG - if(fifo[0]->getSemValue()>(fifoSize-100)) - cprintf(CYAN,"Fifo[%d]: value:%d, pop 0x%x\n",0,fifo[0]->getSemValue(),(void*)(wbuf[0])); + if(fifo[listenfifoIndex]->getSemValue()>(fifoSize-100)) + cprintf(CYAN,"Fifo[%d]: value:%d, pop 0x%x\n",listenfifoIndex,fifo[listenfifoIndex]->getSemValue(),(void*)(wbuf)); #endif -#ifdef DEBUG5 - cprintf(GREEN,"Writing_Thread %d: Popped %p from FIFO %d\n", ithread, (void*)(wbuf[0]),0); -#endif - uint32_t numPackets = (uint32_t)(*((uint32_t*)wbuf[0])); #ifdef DEBUG4 - cprintf(GREEN,"Writing_Thread %d: Number of Packets: %d for FIFO %d\n", ithread, numPackets, 0); + cprintf(GREEN,"Writing_Thread %d: Popped %p from FIFO %d\n", ithread, (void*)(wbuf),listenfifoIndex); +#endif + uint32_t numPackets = (uint32_t)(*((uint32_t*)wbuf)); +#ifdef DEBUG4 + cprintf(GREEN,"Writing_Thread %d: Number of Packets: %d for FIFO %d\n", ithread, numPackets, listenfifoIndex); #endif //end of acquisition if(numPackets == dummyPacketValue){ -#ifdef DEBUG3 - cprintf(GREEN,"Writing_Thread %d: Dummy frame popped out of FIFO %d",ithread, 0); +#ifdef DEBUG4 + cprintf(GREEN,"Writing_Thread %d: Dummy frame popped out of FIFO %d",ithread, listenfifoIndex); #endif stopWriting(ithread,wbuf); continue; @@ -2055,9 +2034,11 @@ void UDPStandardImplementation::processWritingBuffer(int ithread){ - //process + //normal if(!dataCompressionEnable) handleWithoutDataCompression(ithread, wbuf, numPackets); + + //compression else{ #if defined(MYROOT1) && defined(ALLFILE_DEBUG) if(npackets > 0) @@ -2145,12 +2126,13 @@ void UDPStandardImplementation::processWritingBufferPacketByPacket(int ithread){ #endif } delete fifoTempFree[i]; + fifoTempFree[i] = NULL; } fifoTempFree[i] = new CircularFifo(MAX_NUM_PACKETS); } for(uint32_t i=0; ipacketNumber); @@ -2338,7 +2320,7 @@ void UDPStandardImplementation::processWritingBufferPacketByPacket(int ithread){ if(fullframe[0] && fullframe[1]){ currentFrameNumber = presentFrameNumber; numTotMissingPacketsInFile += numMissingPackets; - numTotMissingPackets += numMissingPackets; + numTotMissingPackets += numMissingPackets;/**requires a lock*/ /* cprintf(CYAN,"**framenum:%lld\n ",(long long int)currentFrameNumber); @@ -2379,7 +2361,7 @@ void UDPStandardImplementation::processWritingBufferPacketByPacket(int ithread){ //ensuring last packet got is not of some other future frame but of the current one eiger_packet_footer_t* wbuf_footer1 = (eiger_packet_footer_t*)(packetBuffer[i] + footerOffset + HEADER_SIZE_NUM_TOT_PACKETS); - uint64_t packfnum = (((uint32_t)(*( (uint64_t*) wbuf_footer1)))+(startFrameIndex[ithread] - 1)); + uint64_t packfnum = (((uint32_t)(*( (uint64_t*) wbuf_footer1)))+(startFrameIndex - 1)); //to reset to get new frame: not dummy and the last packet if((numPackets[i] != dummyPacketValue) && (currentPacketNumber[i] == LAST_PACKET_VALUE) && (packfnum == currentFrameNumber) ) @@ -2592,25 +2574,24 @@ bool UDPStandardImplementation::popAndCheckEndofAcquisition(int ithread, char* w -void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer[]){ +void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ FILE_LOG(logDEBUG) << __AT__ << " called"; FILE_LOG(logINFO) << "Writing "<< ithread << ": End of Acquisition"; //free fifo - for(int i=0; ipush(wbuffer[i])); + while(!fifoFree[ithread]->push(wbuffer)); #ifdef EVERYFIFODEBUG - if(fifoFree[i]->getSemValue()<100) - cprintf(GREEN,"FifoFree[%d]: value:%d, push 0x%x\n",i,fifoFree[i]->getSemValue(),(void*)(wbuffer[i])); + if(fifoFree[ithread]->getSemValue()<100) + cprintf(GREEN,"FifoFree[%d]: value:%d, push 0x%x\n",ithread,fifoFree[ithread]->getSemValue(),(void*)(wbuffer)); #endif #ifdef CFIFODEBUG - if(i==0) - cprintf(CYAN,"Writing_Thread %d: Freeing dummy-end buffer. Pushed into fifofree %p for listener %d\n", ithread,(void*)(wbuffer[i]),i); - else - cprintf(YELLOW,"Writing_Thread %d: Freeing dummy-end buffer. Pushed into fifofree %p for listener %d\n", ithread,(void*)(wbuffer[i]),i); + if(ithread==0) + cprintf(CYAN,"Writing_Thread %d: Freeing dummy-end buffer. Pushed into fifofree %p for listener %d\n", ithread,(void*)(wbuffer),ithread); + else + cprintf(YELLOW,"Writing_Thread %d: Freeing dummy-end buffer. Pushed into fifofree %p for listener %d\n", ithread,(void*)(wbuffer),ithread); #endif - } + //all threads need to close file, reset mask and exit loop closeFile(ithread); @@ -2644,7 +2625,7 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer[]){ //statistics FILE_LOG(logINFO) << "Status: Run Finished"; - FILE_LOG(logINFO) << "Last Frame Number Caught:" << lastFrameIndex; + FILE_LOG(logINFO) << "Last Frame Number Caught:" << lastFrameIndex[ithread]; if(totalPacketsCaught < ((uint64_t)numberOfFrames*packetsPerFrame)){ cprintf(RED, "Total Missing Packets padded: %d\n",numTotMissingPackets); cprintf(RED, "Total Packets Caught: %lld\n",(long long int)totalPacketsCaught); @@ -2663,7 +2644,7 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer[]){ -void UDPStandardImplementation::handleWithoutDataCompression(int ithread, char* wbuffer[],uint32_t npackets){ +void UDPStandardImplementation::handleWithoutDataCompression(int ithread, char* wbuffer,uint32_t npackets){ FILE_LOG(logDEBUG) << __AT__ << " called"; @@ -2682,7 +2663,7 @@ void UDPStandardImplementation::handleWithoutDataCompression(int ithread, char* } //set indices acquisitionIndex = currentFrameNumber - startAcquisitionIndex; - frameIndex = currentFrameNumber - startFrameIndex[ithread]; + frameIndex = currentFrameNumber - startFrameIndex; } @@ -2774,7 +2755,7 @@ void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* w //set indices acquisitionIndex = currentFrameNumber - startAcquisitionIndex; - frameIndex = currentFrameNumber - startFrameIndex[ithread]; + frameIndex = currentFrameNumber - startFrameIndex; } #ifdef DEBUG3 cprintf(GREEN,"Writing_Thread: Current Frame Number:%d\n",currentFrameNumber); @@ -3065,7 +3046,7 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer pthread_mutex_unlock(&progressMutex); //set indices acquisitionIndex = currentFrameNumber - startAcquisitionIndex; - frameIndex = currentFrameNumber - startFrameIndex[0]; + frameIndex = currentFrameNumber - startFrameIndex; //variable definitions From be2bc15ab5c5e26adb72275610900e206e9cba5b Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Tue, 30 Aug 2016 16:10:46 +0200 Subject: [PATCH 10/48] not done --- .../include/UDPBaseImplementation.h | 2 +- .../include/UDPStandardImplementation.h | 89 +- slsReceiverSoftware/include/genericSocket.h | 24 +- slsReceiverSoftware/include/receiver_defs.h | 2 +- .../src/UDPStandardImplementation.cpp | 1224 ++++++----------- .../src/slsReceiverTCPIPInterface.cpp | 5 +- 6 files changed, 505 insertions(+), 841 deletions(-) diff --git a/slsReceiverSoftware/include/UDPBaseImplementation.h b/slsReceiverSoftware/include/UDPBaseImplementation.h index f9c5e9273..6083b0723 100644 --- a/slsReceiverSoftware/include/UDPBaseImplementation.h +++ b/slsReceiverSoftware/include/UDPBaseImplementation.h @@ -513,7 +513,7 @@ class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInter //***acquisition count parameters*** /** Total packets caught for an entire acquisition (including all scans) */ uint64_t totalPacketsCaught; - /** Frames Caught for each real time acquisition (eg. for each scan) */ + /** Packets Caught for each real time acquisition (eg. for each scan) */ uint64_t packetsCaught; //***acquisition indices parameters*** diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index 587b88dbd..fcec7fc0d 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -78,6 +78,19 @@ class UDPStandardImplementation: private virtual slsReceiverDefs, public UDPBase */ int setDataCompressionEnable(const bool b); + //***acquisition count parameters*** + /** + * Get Total Frames Caught for an entire acquisition (including all scans) + * @return total number of frames caught for entire acquisition + */ + uint64_t getTotalFramesCaught() const; + + /** + * Get Frames Caught for each real time acquisition (eg. for each scan) + * @return number of frames caught for each scan + */ + uint64_t getFramesCaught() const; + //***acquisition parameters*** /** * Overridden method @@ -394,18 +407,6 @@ private: */ void startWriting(); - /** - * Called by processWritingBuffer and processWritingBufferPacketByPacket - * Pops buffer from all the FIFOs and checks for dummy frames and end of acquisition - * @param ithread current thread index - * @param wbuffer the buffer array that is popped from all the FIFOs - * @param ready if that FIFO is allowed to pop (depends on if dummy buffer already popped/ waiting for other FIFO to finish a frame(eiger)) - * @param nP number of packets in the buffer popped out - * @param fifoTempFree circular fifo to save addresses of packets adding upto a frame before pushing into fifofree (eiger specific) - * @return true if end of acquisition else false - */ - bool popAndCheckEndofAcquisition(int ithread, char* wbuffer[], bool ready[], uint32_t nP[],CircularFifo* fifoTempFree[]); - /** * Called by processWritingBuffer and processWritingBufferPacketByPacket * When dummy-end buffers are popped from all FIFOs (acquisition over), this is called @@ -434,14 +435,14 @@ private: * @param wbuffer is the address of buffer popped out of FIFO * @param numpackets is the number of packets */ - void writeFileWithoutCompression(int ithread, char* wbuffer[],uint32_t numpackets); + void writeFileWithoutCompression(int ithread, char* wbuffer,uint32_t numpackets); /** * Called by writeToFileWithoutCompression * Create headers for file writing (at the moment, this is eiger specific) * @param wbuffer writing buffer popped from FIFOs */ - void createHeaders(char* wbuffer[]); + void createHeaders(char* wbuffer); /** * Updates the file header char aray, each time the corresp parameter is changed @@ -456,11 +457,7 @@ private: * @param ithread writer thread index * @param buffer buffer to copy */ - void copyFrameToGui(int ithread, char* buffer[]); - - void processWritingBuffer(int ithread); - - void processWritingBufferPacketByPacket(int ithread); + void copyFrameToGui(int ithread, char* buffer); void waitWritingBufferForNextAcquisition(int ithread); @@ -473,10 +470,28 @@ private: * @param wbuffer writer buffer * @param nf number of frames */ - void handleDataCompression(int ithread, char* wbuffer[], uint64_t &nf); + void handleDataCompression(int ithread, char* wbuffer, uint64_t &nf); + /** + * Get Frame Number + * @param ithread writer thread index + * @param wbuffer writer buffer + * @param tempframenumber reference to the frame number + * @return OK or FAIL + */ + int getFrameNumber(int ithread, char* wbuffer, uint64_t &tempframenumber); + /** + * Find offset upto this frame number and write it to file + * @param ithread writer thread index + * @param wbuffer writer buffer + * @param offset reference of offset to look from and replaces offset to starting of nextframenumber + * @param nextFrameNumber frame number up to which data written + * @param numpackets number of packets in buffer + * @param numPacketsWritten number of packets written to file + */ + int writeUptoFrameNumber(int ithread, char* wbuffer, int &offset, uint64_t nextFrameNumber, uint32_t numpackets, int &numPacketsWritten); /************************************************************************* * Class Members ********************************************************* @@ -526,8 +541,11 @@ private: /** Complete File name */ char completeFileName[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; + /** File Name without frame index, file index and extension (_d0_f000000000000_8.raw)*/ + char fileNamePerThread[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; + /** Maximum Frames Per File **/ - int maxFramesPerFile; + uint64_t maxFramesPerFile; /** If file created successfully for all Writer Threads */ bool fileCreateSuccess; @@ -550,12 +568,12 @@ private: /** Current Frame Number */ uint64_t currentFrameNumber[MAX_NUMBER_OF_WRITER_THREADS]; - /** Previous Frame number from buffer to calculate loss */ - int64_t previousFrameNumber[MAX_NUMBER_OF_WRITER_THREADS]; + int64_t frameNumberInPreviousFile[MAX_NUMBER_OF_WRITER_THREADS]; /** Last Frame Index Listened To */ - int32_t lastFrameIndex[MAX_NUMBER_OF_WRITER_THREADS]; + int64_t lastFrameIndex[MAX_NUMBER_OF_WRITER_THREADS]; + /* Acquisition started */ @@ -564,25 +582,14 @@ private: /* Measurement started - for each thread to get progress print outs*/ bool measurementStarted[MAX_NUMBER_OF_LISTENING_THREADS]; - /** Total Frame Count listened to by listening threads */ - int totalListeningFrameCount[MAX_NUMBER_OF_LISTENING_THREADS]; + /** Total packet Count listened to by listening threads */ + int totalListeningPacketCount[MAX_NUMBER_OF_LISTENING_THREADS]; /** Pckets currently in current file, starts new file when it reaches max */ - uint32_t packetsInFile[MAX_NUMBER_OF_WRITER_THREADS]; - - /** Number of Missing Packets per buffer*/ - uint32_t numMissingPackets[MAX_NUMBER_OF_WRITER_THREADS]; - - /** Total Number of Missing Packets in acquisition*/ - uint32_t numTotMissingPackets; - - /** Number of Missing Packets in file */ - uint32_t numTotMissingPacketsInFile[MAX_NUMBER_OF_WRITER_THREADS]; - - /** packets caught per thread */ - uint64_t packetsCaughtPerThread[MAX_NUMBER_OF_WRITER_THREADS]; - + uint64_t lastFrameNumberInFile[MAX_NUMBER_OF_WRITER_THREADS]; + /** packets in current file */ + uint64_t totalPacketsInFile[MAX_NUMBER_OF_WRITER_THREADS]; @@ -721,8 +728,6 @@ private: /** Progress (currentFrameNumber) Mutex */ pthread_mutex_t progressMutex; - /** Progress (currentFrameNumber) Mutex */ - pthread_mutex_t udpSocketMutex[MAX_NUMBER_OF_LISTENING_THREADS]; //***callback*** /** The action which decides what the user and default responsibilities to save data are diff --git a/slsReceiverSoftware/include/genericSocket.h b/slsReceiverSoftware/include/genericSocket.h index 3facb0a2b..0f52c0413 100644 --- a/slsReceiverSoftware/include/genericSocket.h +++ b/slsReceiverSoftware/include/genericSocket.h @@ -613,25 +613,13 @@ enum communicationProtocol{ while(length>0){ nsending = (length>packet_size) ? packet_size:length; - /* - //created for debugging on 11.05.2015 - nsending=5000; - nsent = recvfrom(socketDescriptor,(char*)buf,nsending, 0, (struct sockaddr *) &clientAddress, &clientAddress_length); - if(nsent <1000){ - if(nsent < 48){ - cout << " "<fnum)<< "\t"; - cout << k <<" packets" << endl; - k = 0; - } - } - else - k++; - */ nsent = recvfrom(socketDescriptor,(char*)buf+total_sent,nsending, 0, (struct sockaddr *) &clientAddress, &clientAddress_length); - if(!nsent) break; + if(nsent < packet_size) { + if(nsent){ + cout << "Incomplete Packet size " << nsent << endl; + } + break; + } length-=nsent; total_sent+=nsent; } diff --git a/slsReceiverSoftware/include/receiver_defs.h b/slsReceiverSoftware/include/receiver_defs.h index 4c621a67f..0c60b892c 100755 --- a/slsReceiverSoftware/include/receiver_defs.h +++ b/slsReceiverSoftware/include/receiver_defs.h @@ -139,7 +139,7 @@ typedef struct { #define JFRAU_BUFFER_SIZE (JFRAU_ONE_PACKET_SIZE*JFRAU_PACKETS_PER_FRAME) //8214*128 -#define JFRAU_FRAME_INDEX_MASK 0x0 //Not Applicable, use struct +#define JFRAU_FRAME_INDEX_MASK 0xffffff //mask after using struct (48 bit) #define JFRAU_FRAME_INDEX_OFFSET 0x0 //Not Applicable, use struct #define JFRAU_PACKET_INDEX_MASK 0x0//Not Applicable, use struct diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 62da115d3..887b827a4 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -10,8 +10,6 @@ #include "gotthardModuleData.h" #include "gotthardShortModuleData.h" -#include "fileIOStatic.h" - #include // exit() #include //set precision for printing parameters for create new file #include //map @@ -38,8 +36,6 @@ UDPStandardImplementation::UDPStandardImplementation(){ pthread_mutex_init(&writeMutex,NULL); pthread_mutex_init(&dataReadyMutex,NULL); pthread_mutex_init(&progressMutex,NULL); - for(int i=0;iShutDownSocket(); FILE_LOG(logINFO) << "Shut down UDP Socket " << i; delete udpSocket[i]; udpSocket[i] = NULL; - pthread_mutex_unlock(&udpSocketMutex[i]); } } return OK; @@ -998,10 +1005,10 @@ void UDPStandardImplementation::startReadout(){ //check if all packets got int totalP = 0,prev,i; for(i=0; iinitEventTree(temp, &iframe); //resets the pedestalSubtraction array and the commonModeSubtraction singlePhotonDetectorObject[ithread]->newDataSet(); @@ -1564,20 +1570,16 @@ void UDPStandardImplementation::startListening(){ #endif - pthread_mutex_lock(&udpSocketMutex[ithread]); //udpsocket doesnt exist - if(udpSocket[ithread] == NULL){ + if(status == TRANSMITTING){ FILE_LOG(logERROR) << "Listening_Thread " << ithread << ": UDP Socket not created or shut down earlier"; stopListening(ithread,0); - pthread_mutex_unlock(&udpSocketMutex[ithread]); continue; } rc = prepareAndListenBuffer(ithread, carryonBufferSize, tempBuffer); carryonBufferSize = 0; - pthread_mutex_unlock(&udpSocketMutex[ithread]); - //start indices for each start of scan/acquisition if((!measurementStarted) && (rc > 0)) startFrameIndices(ithread); @@ -1589,7 +1591,7 @@ void UDPStandardImplementation::startListening(){ //write packet count to buffer if(myDetectorType == EIGER) - (*((uint32_t*)(buffer[ithread]))) = rc/onePacketSize; + (*((uint32_t*)(buffer[ithread]))) = (rc/onePacketSize); if(dataCompressionEnable) (*((uint32_t*)(buffer[ithread]))) = processListeningBuffer(ithread, carryonBufferSize, tempBuffer, rc); @@ -1634,12 +1636,18 @@ void UDPStandardImplementation::startListening(){ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, char* temp){ FILE_LOG(logDEBUG) << __AT__ << " called"; + int receivedSize = 0; + //carry over from previous buffer if(cSize) memcpy(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS, temp, cSize); - int receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); + if(status != TRANSMITTING) + receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); + //throw away packets that is not one packet size - while(myDetectorType == EIGER && receivedSize != onePacketSize) { + while((myDetectorType == EIGER) && + (receivedSize != ((bufferSize * numberofJobsPerBuffer) - cSize)) && + (status != TRANSMITTING)) { if(receivedSize != EIGER_HEADER_LENGTH) cprintf(RED,"Listening_Thread %d: Listened to a weird packet size %d\n",ithread, receivedSize); #ifdef DEBUG @@ -1647,10 +1655,12 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, ch cprintf(BLUE,"Listening_Thread %d: Listened to a header packet\n",ithread); #endif //listen again - receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS); + if(status != TRANSMITTING) + receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); + //cout<0){ @@ -1761,7 +1771,7 @@ void UDPStandardImplementation::stopListening(int ithread, int numbytes){ //push last non empty buffer into fifo else{ (*((uint32_t*)(buffer[ithread]))) = numbytes/onePacketSize; - totalListeningFrameCount[ithread] += (numbytes/onePacketSize); + totalListeningPacketCount[ithread] += (numbytes/onePacketSize); #ifdef DEBUG cprintf(BLUE,"Listening_Thread %d: Last Buffer numBytes:%d\n",ithread, numbytes); cprintf(BLUE,"Listening_Thread %d: Last Buffer packet count:%d\n",ithread, numbytes/onePacketSize); @@ -1813,7 +1823,7 @@ void UDPStandardImplementation::stopListening(int ithread, int numbytes){ listeningThreadsMask^=(1<> frameIndexOffset), @@ -1909,7 +1919,7 @@ uint32_t UDPStandardImplementation::processListeningBuffer(int ithread, int &cSi case JUNGFRAU: - lastPacketOffset = (((numberofJobsPerBuffer * packetsPerFrame - 1) * onePacketSize) + HEADER_SIZE_NUM_TOT_PACKETS); + lastPacketOffset = (((packetCount - 1) * onePacketSize) + HEADER_SIZE_NUM_TOT_PACKETS); #ifdef DEBUG4 header = (jfrau_packet_header_t*) (buffer[ithread]+HEADER_SIZE_NUM_TOT_PACKETS); cprintf(BLUE, "Listening_Thread: First Header:%d\t First Packet:%d\n", @@ -1971,22 +1981,6 @@ void UDPStandardImplementation::startWriting(){ //let calling function know thread started and obtained current threadStarted = 1; - switch(myDetectorType){ - case EIGER: - processWritingBufferPacketByPacket(ithread); - break; - default: - processWritingBuffer(ithread); - break; - } - -} - - - -void UDPStandardImplementation::processWritingBuffer(int ithread){ - FILE_LOG(logDEBUG) << __AT__ << " called"; - //variable definitions char* wbuf; //buffer popped from FIFO sfilefd[ithread] = NULL; //file pointer @@ -2000,7 +1994,7 @@ void UDPStandardImplementation::processWritingBuffer(int ithread){ //--reset parameters before acquisition nf = 0; - guiData[ithread] = latestData[ithread]; //so that the first frame is always copied + guiData[ithread] = latestData[ithread]; //so that the first frame is always copied if(dataCompressionEnable) listenfifoIndex = 0; //compression has only one listening thread @@ -2060,361 +2054,6 @@ void UDPStandardImplementation::processWritingBuffer(int ithread){ -void UDPStandardImplementation::processWritingBufferPacketByPacket(int ithread){ - FILE_LOG(logDEBUG) << __AT__ << " called"; - - //variable definitions - char* packetBuffer[numberofListeningThreads]; //buffer popped from FIFO - sfilefd = NULL; //file pointer - bool popReady[numberofListeningThreads]; //if the FIFO can be popped - uint32_t numPackets[numberofListeningThreads]; //number of packets popped from the FIFO - - int MAX_NUM_PACKETS = 1024; //highest 32 bit has 1024 number of packets - uint32_t LAST_PACKET_VALUE; //last packet number - - CircularFifo* fifoTempFree[numberofListeningThreads];//ciruclar fifo to keep track of one frame packets to be freed and reused later - char* temp = NULL; - - char* frameBuffer[MAX_NUM_PACKETS]; //buffer offset created for a whole frame - int frameBufferoffset[numberofListeningThreads]; //buffer offset created for a whole frame for both listening threads - char* blankframe[MAX_NUM_PACKETS]; //blank buffer for a whole frame with missing packets - int blankoffset; //blank buffer offset - - bool fullframe[numberofListeningThreads]; //if full frame processed for each listening thread - volatile uint32_t threadFrameNumber[numberofListeningThreads]; //thread frame number for each listening thread buffer popped out - volatile uint32_t presentFrameNumber; //the current frame number aiming to be built - volatile uint32_t lastPacketNumber[numberofListeningThreads]; //last packet number got - volatile uint32_t currentPacketNumber[numberofListeningThreads];//current packet number - volatile int numberofMissingPackets[numberofListeningThreads]; // number of missing packets in this buffer - - for(int i=0; iisEmpty()){ - fifoTempFree[i]->pop(temp); -#ifdef EVERYFIFODEBUG - if(fifoTempFree[i]->getSemValue()>((packetsPerFrame/numberofListeningThreads)-3)) - cprintf(RED,"FifoTempFree[%d]: value:%d, pop 0x%x\n",i,fifoTempFree[i]->getSemValue(),(void*)(temp)); -#endif - } - delete fifoTempFree[i]; - fifoTempFree[i] = NULL; - } - fifoTempFree[i] = new CircularFifo(MAX_NUM_PACKETS); - } - - for(uint32_t i=0; imissingPacket) = missingPacketValue; - *( (uint16_t*) blankframe_footer->packetNumber) = i+1; - - //set each value inside blank frame to 0xff - for(int j=0;j<(oneDataSize);++j){ - unsigned char* blankframe_data = (unsigned char*)blankframe[i] + sizeof(eiger_packet_header_t) + j; - *(blankframe_data) = 0xFF; - } - } - //last frame read out - lastFrameIndex = -1; - - - - - /* inner loop - loop for each buffer */ - //until mask unset (udp sockets shut down by client) - while((1 << ithread) & writerThreadsMask){ - - - //pop fifo and if end of acquisition - //cprintf(BLUE,"popready[0]:%d popready[1]:%d\n",popReady[0],popReady[1]); - if(popAndCheckEndofAcquisition(ithread, packetBuffer, popReady, numPackets,fifoTempFree)){ -#ifdef DEBUG4 - cprintf(GREEN,"Writing_Thread All dummy-end buffers popped\n"); -#endif - //finish missing packets - if(((frameBufferoffset[0]!=0) || (frameBufferoffset[1]!=((int)packetsPerFrame/numberofListeningThreads)))); - else{ - stopWriting(ithread,packetBuffer); - continue; - } - } -#ifdef DEBUG4 - else{cprintf(BLUE,"POPped but i see?\n");} -#endif - - //get a full frame------------------------------------------------------------------------------------------------------- - for(int i=0;ipacketNumber); -#ifdef DEBUG4 - cprintf(MAGENTA,"Fifo %d: threadframenumber original:%d currentpacketnumber real:%d\n", - i,threadFrameNumber[i],currentPacketNumber[i]); -#endif - } - - //calculate number of missing packets----------------------------------------------------- - numberofMissingPackets[i] = 0; - if((numPackets[i] == dummyPacketValue) || (threadFrameNumber[i] != presentFrameNumber)) - numberofMissingPackets[i] = (LAST_PACKET_VALUE - lastPacketNumber[i]); - else - numberofMissingPackets[i] = (currentPacketNumber[i] - lastPacketNumber[i] - 1); - numMissingPackets += numberofMissingPackets[i]; - -#ifdef DEBUG4 - if(numPackets[i] == dummyPacketValue) - cprintf(GREEN, "Fifo %d: Calc missing packets (Dummy): Adding missing packets %d to the last frame\n", - i, numberofMissingPackets[i]); - else{ - cprintf(GREEN,"Fifo %d: Calc missing packets: fnum %d, fnum_thread %d, " - "pnum %d, last_pnum %d, pnum_offset %d missing_packets %d\n", - i,presentFrameNumber,threadFrameNumber[i], - currentPacketNumber[i],lastPacketNumber[i],frameBufferoffset[i],numberofMissingPackets[i]); - } -#endif - - - //add missing packets--------------------------------------------------------------------- - for(int j=0;jmissingPacket)!= missingPacketValue){ - eiger_packet_header_t* blankframe_header = (eiger_packet_header_t*) blankframe[blankoffset]; - cprintf(BG_RED, "Fifo %d: Add Missing Packet Error: " - "pnum_offset %d, pnum %d, fnum_thread %d, missingpacket_buffer 0x%x, missingpacket_blank 0x%x\n", - i,frameBufferoffset[i],currentPacketNumber[i],threadFrameNumber[i], - *( (uint16_t*) frameBuffer_header->missingPacket), - *( (uint16_t*) blankframe_header->missingPacket)); - exit(-1); - }else{ -#ifdef DEBUG4 - cprintf(RED, "Fifo %d: Add Missing Packet success: " - "pnum_offset %d, pnum_got %d, fnum_thread %d, missingpacket_buffer 0x%x\n", - i,frameBufferoffset[i],currentPacketNumber[i],threadFrameNumber[i], - *( (uint16_t*) frameBuffer_header->missingPacket)); -#endif - frameBufferoffset[i]=frameBufferoffset[i]+1; - } - } - - //missed packets/future packet: do not pop over and determine fullframe-------------------- - popReady[i] = false; - if((numPackets[i] == dummyPacketValue) ||(threadFrameNumber[i] != presentFrameNumber)) - fullframe[i] = true; - else - fullframe[i] = false; - if(threadFrameNumber[i] != presentFrameNumber) - threadFrameNumber[i] = presentFrameNumber; - - - //add current packet-------------------------------------------------------------- - if(fullframe[i] == false){ - if(currentPacketNumber[i] != (uint32_t)(frameBufferoffset[i]-(i*packetsPerFrame/numberofListeningThreads))+1){ - cprintf(BG_RED, "Fifo %d: Correct Packet Offset Error: " - "pnum_offset %d,pnum %d fnum_thread %d\n", - i,frameBufferoffset[i],currentPacketNumber[i],threadFrameNumber[i]); - exit(-1); - } - - - while(!fifoTempFree[i]->push(packetBuffer[i])); -#ifdef EVERYFIFODEBUG - if(fifoTempFree[i]->getSemValue()>((packetsPerFrame/numberofListeningThreads)-3)) - cprintf(YELLOW,"FifoTempfree[%d]: value:%d, push 0x%x\n",i,fifoTempFree[i]->getSemValue(),(void*)(wbuffer[i])); -#endif - - - - //cprintf(RED,"Current Packet frameBufferoffset[i]:%d\n",frameBufferoffset[i]); - frameBuffer[frameBufferoffset[i]] = (packetBuffer[i] + HEADER_SIZE_NUM_TOT_PACKETS); -#ifdef DEBUG4 - eiger_packet_header_t* frameBuffer_header = (eiger_packet_header_t*) frameBuffer[frameBufferoffset[i]]; - eiger_packet_footer_t* frameBuffer_footer = (eiger_packet_footer_t*) (frameBuffer[frameBufferoffset[i]] + footerOffset); - cprintf(GREEN, "Fifo %d: Current Packet added success:" - "pnum_offset %d, pnum %d, real pnum %d fnum_thread %d, missingpacket_buffer 0x%x\n", - i,frameBufferoffset[i],currentPacketNumber[i],*( (uint16_t*) frameBuffer_footer->packetNumber),threadFrameNumber[i], - *( (uint16_t*) frameBuffer_header->missingPacket)); -#endif - frameBufferoffset[i]=frameBufferoffset[i]+1; - //update last packet - lastPacketNumber[i] = currentPacketNumber[i]; - popReady[i] = true; - fullframe[i] = false; - if(currentPacketNumber[i] == LAST_PACKET_VALUE){ -#ifdef DEBUG4 - cprintf(GREEN, "Fifo %d: Got last packet\n",i); -#endif - popReady[i] = false; - fullframe[i] = true; - } //end of last packet - }//end of add current packet - }//end of if(!fullframe) - }//end of for listening threads - - - //full frame - if(fullframe[0] && fullframe[1]){ - currentFrameNumber = presentFrameNumber; - numTotMissingPacketsInFile += numMissingPackets; - numTotMissingPackets += numMissingPackets;/**requires a lock*/ - -/* - cprintf(CYAN,"**framenum:%lld\n ",(long long int)currentFrameNumber); - if(currentFrameNumber>500){ - cprintf(BG_RED,"too high frame number %lld \n",(long long int)currentFrameNumber ); - exit(-1); - } - for(int i=0;ipacketNumber), (void*)(packetBuffer[i])); - }*/ -#ifdef DEBUG4 - cprintf(BLUE," nummissingpackets:%d\n",numMissingPackets); -#endif -#ifdef FNUM_DEBUG - cprintf(GREEN,"**fnum:%lld**\n",(long long int)currentFrameNumber); -#endif -#ifdef MISSINGP_DEBUG - if(numMissingPackets){ - cprintf(RED, "Total missing packets %d for fnum %d\n",numMissingPackets,currentFrameNumber); - for (int j=0;jmissingPacket)==missingPacketValue) - cprintf(RED,"Found missing packet at pnum %d\n",j); - } - } -#endif - - //write and copy to gui - handleWithoutDataCompression(ithread,frameBuffer,packetsPerFrame); - - //reset a few stuff - presentFrameNumber++; - for(int i=0; iisEmpty()){ - fifoTempFree[i]->pop(temp); -#ifdef EVERYFIFODEBUG - if(fifoTempFree[i]->getSemValue()>((packetsPerFrame/numberofListeningThreads)-3)) - cprintf(GRAY,"FifoTempFree[%d]: value:%d, pop 0x%x\n",i,fifoTempFree[i]->getSemValue(),(void*)(temp)); -#endif - while(!fifoFree[i]->push(temp)); -#ifdef EVERYFIFODEBUG - if(fifoFree[i]->getSemValue()<100) - cprintf(GREEN,"FifoFree[%d]: value:%d, push 0x%x\n",i,fifoFree[i]->getSemValue(),(void*)(temp)); -#endif -#ifdef CFIFODEBUG - if(i==0) - cprintf(CYAN,"Fifo %d: Writing_Thread freed: pushed into fifofree %p\n",i, (void*)(temp)); - else - cprintf(YELLOW,"Fifo %d: Writing_Thread freed: pushed into fifofree %p\n",i, (void*)(temp)); -#endif - } - } -#ifdef DEBUG4 - cprintf(GREEN,"Writing_Thread: finished freeing\n"); -#endif - - - }//end of full frame - - }/*--end of loop for each buffer (inner loop)*/ - - waitWritingBufferForNextAcquisition(ithread); - - }/*--end of loop for each acquisition (outer loop) */ -} - - - - void UDPStandardImplementation::waitWritingBufferForNextAcquisition(int ithread){ FILE_LOG(logDEBUG) << __AT__ << " called"; @@ -2451,24 +2090,20 @@ void UDPStandardImplementation::waitWritingBufferForNextAcquisition(int ithread) //change the detector index in the file names if(myDetectorType == EIGER){ int detindex = -1; - string tempname(fileName[ithread]); - cout<<"tempname:"<* fifoTempFree[]){ - FILE_LOG(logDEBUG) << __AT__ << " called"; - - bool endofAcquisition = true; - for(int i=0; ipop(wbuffer[i]); -#ifdef EVERYFIFODEBUG - if(fifo[i]->getSemValue()>(fifoSize-100)) - cprintf(CYAN,"Fifo[%d]: value:%d, pop 0x%x\n",i,fifo[i]->getSemValue(),(void*)(wbuffer[i])); -#endif -#ifdef CFIFODEBUG - if(i == 0) - cprintf(CYAN,"Writing_Thread %d: Popped %p from FIFO %d\n", ithread, (void*)(wbuffer[i]),i); - else - cprintf(YELLOW,"Writing_Thread %d: Popped %p from FIFO %d\n", ithread, (void*)(wbuffer[i]),i); -#endif - nP[i] = (uint32_t)(*((uint32_t*)wbuffer[i])); -#ifdef DEBUG4 - cprintf(GREEN,"Writing_Thread %d: Number of Packets: %d for FIFO %d\n", ithread, nP[i], i); -#endif - //dummy-end buffer - if(nP[i] == dummyPacketValue){ - ready[i] = false; -#ifdef DEBUG3 - cprintf(GREEN,"Writing_Thread %d: Dummy frame popped out of FIFO %d",ithread, i); -#endif - } - //normal buffer popped out - else{ - endofAcquisition = false; -#ifdef DEBUG4 - if(myDetectorType == EIGER){ - eiger_packet_footer_t* wbuf_footer = (eiger_packet_footer_t*)(wbuffer[i] + footerOffset + HEADER_SIZE_NUM_TOT_PACKETS); - //cprintf(BLUE,"footer value:0x%x\n",i,(uint64_t)(*( (uint64_t*) wbuf_footer))); - //if(*( (uint16_t*) wbuf_footer->packetNumber) == 1){ - cprintf(BLUE,"Fnum[%d]:%d\n",i,(uint32_t)(*( (uint64_t*) wbuf_footer))); - cprintf(BLUE,"Pnum[%d]:%d\n",i,*( (uint16_t*) wbuf_footer->packetNumber)); - //} - } -#endif - } - } - //when both are not popped but curretn frame number is being processed - else{ - if(nP[i] != dummyPacketValue) - endofAcquisition = false; - } - } - - return endofAcquisition; -} - - void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ FILE_LOG(logDEBUG) << __AT__ << " called"; @@ -2626,294 +2206,200 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ //statistics FILE_LOG(logINFO) << "Status: Run Finished"; FILE_LOG(logINFO) << "Last Frame Number Caught:" << lastFrameIndex[ithread]; - if(totalPacketsCaught < ((uint64_t)numberOfFrames*packetsPerFrame)){ - cprintf(RED, "Total Missing Packets padded: %d\n",numTotMissingPackets); + if(totalPacketsCaught < ((uint64_t)numberOfFrames*packetsPerFrame*numberofListeningThreads)){ + cprintf(RED, "Total Missing Packets: %lld\n",(long long int)numberOfFrames*packetsPerFrame*numberofListeningThreads-totalPacketsCaught); cprintf(RED, "Total Packets Caught: %lld\n",(long long int)totalPacketsCaught); - cprintf(RED, "Total Frames Caught: %lld\n",(long long int)(totalPacketsCaught/packetsPerFrame)); + cprintf(RED, "Total Frames Caught: %lld\n",(long long int)(totalPacketsCaught/(packetsPerFrame*numberofListeningThreads))); }else{ - cprintf(GREEN, "Total Missing Packets padded: %d\n",numTotMissingPackets); - cprintf(GREEN, "Total Packets Caught:%lld\n", (long long int)totalPacketsCaught); - cprintf(GREEN, "Total Frames Caught:%lld\n",(long long int)(totalPacketsCaught/packetsPerFrame)); + cprintf(GREEN, "Total Missing Packets: %lld\n",(long long int)numberOfFrames*packetsPerFrame*numberofListeningThreads-totalPacketsCaught); + cprintf(GREEN, "Total Packets Caught: %lld\n",(long long int)totalPacketsCaught); + cprintf(GREEN, "Total Frames Caught: %lld\n",(long long int)(totalPacketsCaught/(packetsPerFrame*numberofListeningThreads))); } //acquisition end if (acquisitionFinishedCallBack) - acquisitionFinishedCallBack((int)(totalPacketsCaught/packetsPerFrame), pAcquisitionFinished); + acquisitionFinishedCallBack((int)totalPacketsCaught, pAcquisitionFinished); } } -void UDPStandardImplementation::handleWithoutDataCompression(int ithread, char* wbuffer,uint32_t npackets){ +void UDPStandardImplementation::handleWithoutDataCompression(int ithread, char* wbuffer, uint32_t npackets){ FILE_LOG(logDEBUG) << __AT__ << " called"; - //get frame number (eiger already gets it when it does packet to packet processing) - if(myDetectorType != EIGER){ - if(myDetectorType == JUNGFRAU){ - jfrau_packet_header_t* header = (jfrau_packet_header_t*)(wbuffer[0] + HEADER_SIZE_NUM_TOT_PACKETS); - currentFrameNumber = (*( (uint32_t*) header->frameNumber))&0xffffff; - }else{ - uint64_t tempframenumber = ((uint32_t)(*((uint32_t*)(wbuffer[0] + HEADER_SIZE_NUM_TOT_PACKETS)))); - //for gotthard and normal frame, increment frame number to separate fnum and pnum - if (myDetectorType == PROPIX ||(myDetectorType == GOTTHARD && shortFrameEnable == -1)) - tempframenumber++; - //get frame number - currentFrameNumber = (tempframenumber & frameIndexMask) >> frameIndexOffset; - } - //set indices - acquisitionIndex = currentFrameNumber - startAcquisitionIndex; - frameIndex = currentFrameNumber - startFrameIndex; + //get current frame number + uint64_t tempframenumber; + if(getFrameNumber(ithread, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS,tempframenumber) == FAIL){ + //error in frame number sent by fpga + while(!fifoFree[ithread]->push(wbuffer)); + + return; } + //update current frame number + lastFrameIndex[ithread] = tempframenumber; + if(myDetectorType == EIGER) + currentFrameNumber[ithread] = tempframenumber + (startFrameIndex - 1); + else + currentFrameNumber[ithread] = tempframenumber-startFrameIndex; - + //set indices + pthread_mutex_lock(&progressMutex); + if((currentFrameNumber[ithread] - startAcquisitionIndex) > acquisitionIndex) + acquisitionIndex = currentFrameNumber[ithread] - startAcquisitionIndex; + if((currentFrameNumber[ithread] - startFrameIndex) > frameIndex[ithread]) + frameIndex[ithread] = currentFrameNumber[ithread] - startFrameIndex; + pthread_mutex_unlock(&progressMutex); //callback to write data - if (cbAction < DO_EVERYTHING){ - switch(myDetectorType){ - case EIGER: - for(uint32_t i=0;i 0) - writeFileWithoutCompression(wbuffer, npackets); + writeFileWithoutCompression(ithread, wbuffer, npackets); #ifdef DEBUG4 cprintf(GREEN,"Writing_Thread: Writing done\nGoing to copy frame\n"); #endif //copy frame for gui - if(npackets >= packetsPerFrame) - copyFrameToGui(wbuffer); + if(npackets >= packetsPerFrame)/**needs to be reworked*/ + copyFrameToGui(ithread, wbuffer); #ifdef DEBUG4 cprintf(GREEN,"Writing_Thread: Copied frame\n"); #endif - //free fifo addresses (eiger frees for each packet later) - if(myDetectorType != EIGER){ - while(!fifoFree[0]->push(wbuffer[0])); + //free fifo addresses + int listenfifoThread = ithread; + if(dataCompressionEnable) + listenfifoThread = 0; + while(!fifoFree[listenfifoThread]->push(wbuffer)); #ifdef EVERYFIFODEBUG - if(fifoFree[0]->getSemValue()<100) - cprintf(GREEN,"FifoFree[%d]: value:%d, push 0x%x\n",0,fifoFree[0]->getSemValue(),(void*)(wbuffer[0])); + if(fifoFree[listenfifoThread]->getSemValue()<100) + cprintf(GREEN,"FifoFree[%d]: value:%d, push 0x%x\n",listenfifoThread,fifoFree[listenfifoThread]->getSemValue(),(void*)(wbuffer)); #endif #ifdef DEBUG5 - cprintf(GREEN,"Writing_Thread %d: Freed buffer, pushed into fifofree %p for listener 0\n",ithread, (void*)(wbuffer[0])); + cprintf(GREEN,"Writing_Thread %d: Freed buffer, pushed into fifofree %p for listener %d \n",listenfifoThread, (void*)(wbuffer), listenfifoThread); #endif - } + } -void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* wbuffer[],uint32_t numpackets){ +void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* wbuffer,uint32_t numpackets){ FILE_LOG(logDEBUG) << __AT__ << " called"; - - //create headers for eiger -#ifdef WRITE_HEADERS - if (myDetectorType == EIGER && cbAction == DO_EVERYTHING) - createHeaders(wbuffer); -#endif - //if write enabled - if((fileWriteEnable) && (sfilefd)){ - int offset = HEADER_SIZE_NUM_TOT_PACKETS; //offset (not eiger) to keep track of how many packets saved - uint32_t packetsToSave; //how many packets to save at a time - volatile uint64_t tempframenumber; - int lastpacket; + if((fileWriteEnable) && (sfilefd[ithread])){ +cout< 0){ - //new file - if(packetsInFile >= (uint32_t)maxPacketsPerFile){ - //for packet loss, because currframenum is the latest one for eiger - //get frame number (eiger already gets it when it does packet to packet processing) - if(myDetectorType != EIGER){ - lastpacket = (((packetsToSave - 1) * onePacketSize) + offset); - if(myDetectorType == JUNGFRAU){ - jfrau_packet_header_t* header = (jfrau_packet_header_t*) (wbuffer[0] + lastpacket); - currentFrameNumber = (*( (uint32_t*) header->frameNumber))&0xffffff; - }else{ - tempframenumber = ((uint32_t)(*((uint32_t*)(wbuffer[0] + lastpacket)))); - //for gotthard and normal frame, increment frame number to separate fnum and pnum - if (myDetectorType == PROPIX ||(myDetectorType == GOTTHARD && shortFrameEnable == -1)) - tempframenumber++; - //get frame number - currentFrameNumber = (tempframenumber & frameIndexMask) >> frameIndexOffset; - } - - //set indices - acquisitionIndex = currentFrameNumber - startAcquisitionIndex; - frameIndex = currentFrameNumber - startFrameIndex; + //handle half frames from previous buffer + //second part to not check when there has been something written previously + if(numpackets &&(lastFrameNumberInFile[ithread])){ + //get start frame (required to create new file at the right juncture) + uint64_t startframe =-1; + if(getFrameNumber(ithread, wbuffer + offset, startframe) == FAIL){ + //error in frame number sent by fpga + while(!fifoFree[ithread]->push(wbuffer)); + return; } -#ifdef DEBUG3 - cprintf(GREEN,"Writing_Thread: Current Frame Number:%d\n",currentFrameNumber); -#endif - createNewFile(ithread); - } - //to create new file when max reached - packetsToSave = maxPacketsPerFile - packetsInFile; - if(packetsToSave > numpackets) - packetsToSave = numpackets; + cout<<"222"<push(wbuffer)); + return; + } + totalPacketsInFile[ithread] += numpackets; + lastFrameNumberInFile[ithread] = finalLastFrameNumberToSave+1; + currentFrameNumber[ithread] = finalLastFrameNumberToSave; + } + if(numberofWriterThreads > 1) pthread_mutex_lock(&writeMutex); - packetsInFile += numpackets; - packetsCaughtPerThread[ithread] += (numpackets - numMissingPackets); - pthread_mutex_lock(&progressMutex); - packetsCaught += (numpackets - numMissingPackets); - pthread_mutex_unlock(&progressMutex); - totalPacketsCaught += (numpackets - numMissingPackets); - numMissingPackets = 0; + packetsCaught += numpackets; + totalPacketsCaught += numpackets; if(numberofWriterThreads > 1) pthread_mutex_unlock(&writeMutex); } + //set indices + pthread_mutex_lock(&progressMutex); + if((currentFrameNumber[ithread] - startAcquisitionIndex) > acquisitionIndex) + acquisitionIndex = currentFrameNumber[ithread] - startAcquisitionIndex; + if((currentFrameNumber[ithread] - startFrameIndex) > frameIndex[ithread]) + frameIndex[ithread] = currentFrameNumber[ithread] - startFrameIndex; + pthread_mutex_unlock(&progressMutex); } -void UDPStandardImplementation::createHeaders(char* wbuffer[]){ - - - int port = 0, missingPacket; - bool exitVal = 0; - eiger_packet_header_t* wbuf_header; - eiger_packet_footer_t* wbuf_footer; - - for (uint32_t i = 0; i < packetsPerFrame; i++){ - - - wbuf_header = (eiger_packet_header_t*) wbuffer[i]; - wbuf_footer = (eiger_packet_footer_t*)(wbuffer[i] + footerOffset); -#ifdef DEBUG4 - cprintf(GREEN, "Loop index:%d Pnum:%d real fnum %d,missingPacket 0x%x\n", - i, - *( (uint16_t*) wbuf_footer->packetNumber), - (uint32_t)(*( (uint64_t*) wbuf_footer)), - *( (uint16_t*) wbuf_header->missingPacket) - ); cout <missingPacket)== missingPacketValue){ -#ifdef DEBUG4 - cprintf(RED,"-Missing packet at Loop Index %d\n", i); -#endif - missingPacket = 1; - - //DEBUGGING - if(*( (uint16_t*) wbuf_footer->packetNumber) != (i+1)){ - cprintf(BG_RED, "Writing_Thread: Packet Number Mismatch (missing p)! " - "i %d, real pnum %d, real fnum %d, missingPacket 0x%x\n", - i, - *( (uint16_t*) wbuf_footer->packetNumber), - (uint32_t)(*( (uint64_t*) wbuf_footer)), - *( (uint16_t*) wbuf_header->missingPacket)); - exitVal =1; - } - - //add frame number - *( (uint64_t*) wbuf_footer) = (currentFrameNumber+1) | (((uint64_t)(*( (uint16_t*) wbuf_footer->packetNumber)))<<0x30); - //*( (uint16_t*) wbuf_footer->packetNumber) = (i+1); // missing frames already have the right packet number -#ifdef DEBUG4 - cprintf(RED, "Missing Packet Loop index:%d fnum:%d Pnum:%d\n",i, - (uint32_t)(*( (uint64_t*) wbuf_footer)), - *( (uint16_t*) wbuf_footer->packetNumber)); -#endif - } - //normal packet - else{ - missingPacket = 0; - - //DEBUGGING - if(*( (uint16_t*) wbuf_footer->packetNumber) != ( (i>((packetsPerFrame/2)-1)?(i-(packetsPerFrame/2)+1):i+1) )){ - cprintf(BG_RED, "Writing_Thread: Packet Number Mismatch! " - "i %d, real pnum %d, real fnum %d, missingPacket 0x%x\n", - i, - *( (uint16_t*) wbuf_footer->packetNumber), - (uint32_t)(*( (uint64_t*) wbuf_footer)), - *( (uint16_t*) wbuf_header->missingPacket)); - exitVal =1; - } - - uint16_t p = *( (uint16_t*) wbuf_footer->packetNumber); - //correct the packet numbers of port2 so that port1 and 2 are not the same - if(port) *( (uint16_t*) wbuf_footer->packetNumber) = (p +(packetsPerFrame/2)); - - } - - //overwriting port number and dynamic range - *( (uint8_t*) wbuf_header->portIndex) = (uint8_t)port; - //*( (uint8_t*) wbuf_header->dynamicRange) = (uint8_t)dynamicRange; - - //DEBUGGING - if(*( (uint16_t*) wbuf_footer->packetNumber) != (i+1)){ - cprintf(BG_RED, "Writing_Thread: Packet Number Mismatch! " - "i %d, real pnum %d, real fnum %d, missingPacket 0x%x\n", - i, - *( (uint16_t*) wbuf_footer->packetNumber), - (uint32_t)(*( (uint64_t*) wbuf_footer)), - *( (uint16_t*) wbuf_header->missingPacket)); - exitVal =1; - } - } - - if(exitVal){exit(-1);} - -} - void UDPStandardImplementation::updateFileHeader(int ithread){ int xpix=-1,ypix=-1; @@ -2960,23 +2446,23 @@ void UDPStandardImplementation::updateFileHeader(int ithread){ } -void UDPStandardImplementation::copyFrameToGui(char* buffer[]){ +void UDPStandardImplementation::copyFrameToGui(int ithread, char* buffer){ FILE_LOG(logDEBUG) << __AT__ << " called"; //random read (gui not ready) //need to toggle guiDataReady or the second frame wont be copied - if((!FrameToGuiFrequency) && (!guiData)){ + if((!FrameToGuiFrequency) && (!guiData[ithread])){ #ifdef DEBUG4 cprintf(GREEN,"Writing_Thread: CopyingFrame: Resetting guiDataReady\n"); #endif pthread_mutex_lock(&dataReadyMutex); - guiDataReady=0; + guiDataReady[ithread]=0; pthread_mutex_unlock(&dataReadyMutex); } //if nthe frame, wait for your turn (1st frame always shown as its zero) - else if(FrameToGuiFrequency && ((frametoGuiCounter)%FrameToGuiFrequency)); + else if(FrameToGuiFrequency && ((frametoGuiCounter[ithread])%FrameToGuiFrequency)); //random read (gui ready) or nth frame read: gui needs data now or it is the first frame else{ @@ -2984,22 +2470,15 @@ void UDPStandardImplementation::copyFrameToGui(char* buffer[]){ cprintf(GREEN,"Writing_Thread: CopyingFrame: Gui needs data now OR 1st frame\n"); #endif pthread_mutex_lock(&dataReadyMutex); - guiDataReady=0; + guiDataReady[ithread]=0; #ifdef DEBUG4 cprintf(GREEN,"Writing_Thread: CopyingFrame: guidataready is 0, Copying data\n"); #endif - switch(myDetectorType){ - case EIGER: - for(uint32_t i=0; i> frameIndexOffset; - //handle multi threads - pthread_mutex_lock(&progressMutex); - if(tempframenumber > currentFrameNumber) - currentFrameNumber = tempframenumber; - pthread_mutex_unlock(&progressMutex); - //set indices - acquisitionIndex = currentFrameNumber - startAcquisitionIndex; - frameIndex = currentFrameNumber - startFrameIndex; + uint64_t tempframenumber=-1; + if(getFrameNumber(ithread, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS, tempframenumber) == FAIL){ + //error in frame number sent by fpga + while(!fifoFree[ithread]->push(wbuffer)); + return; + } + currentFrameNumber[ithread] = tempframenumber; + //set indices + pthread_mutex_lock(&progressMutex); + if((currentFrameNumber[ithread] - startAcquisitionIndex) > acquisitionIndex) + acquisitionIndex = currentFrameNumber[ithread] - startAcquisitionIndex; + if((currentFrameNumber[ithread] - startFrameIndex) > frameIndex[ithread]) + frameIndex[ithread] = currentFrameNumber[ithread] - startFrameIndex; + pthread_mutex_unlock(&progressMutex); //variable definitions char* buff[2]={0,0}; //an array just to be compatible with copyframetogui - char* data = wbuffer[0]+ HEADER_SIZE_NUM_TOT_PACKETS; //data pointer to the next memory to be analysed + char* data = wbuffer+ HEADER_SIZE_NUM_TOT_PACKETS; //data pointer to the next memory to be analysed int ndata; //size of data returned uint32_t np; //remaining number of packets returned - uint32_t npackets = (uint32_t)(*((uint32_t*)wbuffer[0])); //number of total packets + uint32_t npackets = (uint32_t)(*((uint32_t*)wbuffer)); //number of total packets int remainingsize = npackets * onePacketSize; //size of the memory slot to be analyzed eventType thisEvent = PEDESTAL; @@ -3120,8 +2599,8 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer //cout << "Fill in event: frmNr: " << iFrame << " ix " << ix << " iy " << iy << " type " << thisEvent << endl; #else pthread_mutex_lock(&writeMutex); - if((fileWriteEnable) && (sfilefd)) - singlePhotonDetectorObject[ithread]->writeCluster(sfilefd); + if((fileWriteEnable) && (sfilefd[0])) + singlePhotonDetectorObject[ithread]->writeCluster(sfilefd[0]); pthread_mutex_unlock(&writeMutex); #endif } @@ -3129,38 +2608,229 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer } nf++; + + #ifndef ALLFILE - pthread_mutex_lock(&progressMutex); - packetsInFile += packetsPerFrame; - packetsCaughtPerThread[0] += packetsPerFrame; - packetsCaught += packetsPerFrame; - totalPacketsCaught += packetsPerFrame; - if(packetsInFile >= (uint32_t)maxPacketsPerFile) - createNewFile(0); - pthread_mutex_unlock(&progressMutex); + totalPacketsInFile[ithread] += (bufferSize/packetsPerFrame); + pthread_mutex_lock(&writeMutex); + if((packetsCaught%packetsPerFrame) >= (uint32_t)maxFramesPerFile) + createNewFile(ithread); + packetsCaught += (bufferSize/packetsPerFrame); + totalPacketsCaught += (bufferSize/packetsPerFrame); + pthread_mutex_unlock(&writeMutex); + #endif if(!once){ - copyFrameToGui(buff); + copyFrameToGui(ithread, buff[0]); once = 1; } } remainingsize -= ((buff[0] + ndata) - data); data = buff[0] + ndata; - if(data > (wbuffer[0] + HEADER_SIZE_NUM_TOT_PACKETS + npackets * onePacketSize) ) + if(data > (wbuffer + HEADER_SIZE_NUM_TOT_PACKETS + npackets * onePacketSize) ) cprintf(BG_RED,"Writing_Thread %d: Error: Compression data goes out of bounds!\n", ithread); } - while(!fifoFree[0]->push(wbuffer[0])); + while(!fifoFree[0]->push(wbuffer)); #ifdef EVERYFIFODEBUG if(fifoFree[0]->getSemValue()<100) - cprintf(GREEN,"FifoFree[%d]: value:%d, push 0x%x\n",0,fifoFree[0]->getSemValue(),(void*)(wbuffer[0])); + cprintf(GREEN,"FifoFree[%d]: value:%d, push 0x%x\n",0,fifoFree[0]->getSemValue(),(void*)(wbuffer)); #endif #ifdef DEBUG5 - cprintf(GREEN,"Writing_Thread %d: Compression free pushed into fifofree %p for listerner 0\n", ithread, (void*)(wbuffer[0])); + cprintf(GREEN,"Writing_Thread %d: Compression free pushed into fifofree %p for listerner 0\n", ithread, (void*)(wbuffer)); #endif } + +int UDPStandardImplementation::getFrameNumber(int ithread, char* wbuffer, uint64_t &tempframenumber){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + eiger_packet_footer_t* footer=0; + jfrau_packet_header_t* header=0; + int pnum=-1; + + switch(myDetectorType){ + + case EIGER: + footer = (eiger_packet_footer_t*)(wbuffer + footerOffset); + tempframenumber = (uint32_t)(*( (uint64_t*) footer)); + //error in frame number sent by fpga + if(!((uint32_t)(*( (uint64_t*) footer)))){ + tempframenumber = -1; + FILE_LOG(logERROR) << "Fifo "<< ithread << ": Frame Number is zero from firmware."; + return FAIL; + } +//#ifdef DEBUG4 + if(!ithread) cprintf(GREEN,"Writing_Thread %d: fnum:%lld pnum:%d FPGA_fnum:%d footeroffset:%d\n", + ithread, + (long long int)tempframenumber, + (*( (uint16_t*) footer->packetNumber)), + (uint32_t)(*( (uint64_t*) footer)), + footerOffset); +//#endif + break; + + case JUNGFRAU: + header = (jfrau_packet_header_t*)(wbuffer); + tempframenumber = (*( (uint32_t*) header->frameNumber))&frameIndexMask; +#ifdef DEBUG4 + cprintf(GREEN, "Writing_Thread %d: fnum:%lld\t pnum:%d\n", + (long long int)tempframenumber, + (*( (uint8_t*) header->packetNumber))); +#endif + break; + + default: + tempframenumber = ((uint32_t)(*((uint32_t*)(wbuffer)))); + //for gotthard and normal frame, increment frame number to separate fnum and pnum + if (myDetectorType == PROPIX ||(myDetectorType == GOTTHARD && shortFrameEnable == -1)) + tempframenumber++; + pnum = tempframenumber&packetIndexMask; + tempframenumber = (tempframenumber & frameIndexMask) >> frameIndexOffset; +#ifdef DEBUG4 + cprintf(GREEN, "Writing_Thread %d: fnum:%lld\t pnum:%d\n", + (long long int)tempframenumber, + pnum); +#endif + + break; + } + return OK; +} + + + + + +int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, int &offset, uint64_t nextFrameNumber, uint32_t numpackets, int &numPacketsWritten){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + + bool expectedoffsetATlastpacket = false; + int startoffset = offset; + if(!ithread) cout<= endoffset){ + expectedoffset = startoffset + ((numpackets -1) * onePacketSize); + expectedoffsetATlastpacket = true; + } + offset = expectedoffset; + if(!ithread) cout<push(wbuffer)); + return FAIL; + } + if(!ithread) cout<=nextFrameNumber){ + if(!ithread) cout<=nextFrameNumber){ + offset -= (onePacketSize*packetsPerFrame);/** its ok..if jonbsperthread is 1, go packet by packet*/ + if(!ithread) cout<push(wbuffer)); + return FAIL; + } + if(!ithread) cout<push(wbuffer)); + return FAIL; + } + if(!ithread) cout<endoffset){if(!ithread) cout<push(wbuffer)); + return FAIL; + } + if(!ithread) cout<endoffset){ + offset = endoffset; + if(!ithread) cout< end offset so offset now:"<nextFrameNumber){ + offset -= onePacketSize; + if(!ithread) cout<push(wbuffer)); + return FAIL; + } + if(!ithread) cout<getTotalFramesCaught(); + cout<<"frames caught sent:"<differentClients){ FILE_LOG(logDEBUG) << "Force update"; @@ -2487,9 +2488,9 @@ int slsReceiverTCPIPInterface::set_dynamic_range() { dynamicrange = retval; if(myDetectorType == EIGER){ if(!tenGigaEnable) - packetsPerFrame = EIGER_ONE_GIGA_CONSTANT * dynamicrange * EIGER_MAX_PORTS; + packetsPerFrame = EIGER_ONE_GIGA_CONSTANT * dynamicrange; else - packetsPerFrame = EIGER_TEN_GIGA_CONSTANT * dynamicrange * EIGER_MAX_PORTS; + packetsPerFrame = EIGER_TEN_GIGA_CONSTANT * dynamicrange; }else if (myDetectorType == JUNGFRAU) packetsPerFrame = JFRAU_PACKETS_PER_FRAME; } From fbf6e2bff1695c199e1c31e053dc094ac3097d01 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Wed, 31 Aug 2016 10:23:58 +0200 Subject: [PATCH 11/48] not done --- .../src/UDPStandardImplementation.cpp | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 887b827a4..0e9dc9015 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1643,23 +1643,7 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, ch if(status != TRANSMITTING) receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); - - //throw away packets that is not one packet size - while((myDetectorType == EIGER) && - (receivedSize != ((bufferSize * numberofJobsPerBuffer) - cSize)) && - (status != TRANSMITTING)) { - if(receivedSize != EIGER_HEADER_LENGTH) - cprintf(RED,"Listening_Thread %d: Listened to a weird packet size %d\n",ithread, receivedSize); -#ifdef DEBUG - else - cprintf(BLUE,"Listening_Thread %d: Listened to a header packet\n",ithread); -#endif - //listen again - if(status != TRANSMITTING) - receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); - //cout< Date: Wed, 31 Aug 2016 17:23:44 +0200 Subject: [PATCH 12/48] separated --- .../include/UDPBaseImplementation.h | 3 +- slsReceiverSoftware/include/UDPInterface.h | 3 +- .../include/UDPStandardImplementation.h | 6 +- slsReceiverSoftware/include/genericSocket.h | 20 +- .../src/UDPBaseImplementation.cpp | 2 +- .../src/UDPStandardImplementation.cpp | 267 +++++++++--------- .../src/slsReceiverTCPIPInterface.cpp | 85 +++--- 7 files changed, 205 insertions(+), 181 deletions(-) diff --git a/slsReceiverSoftware/include/UDPBaseImplementation.h b/slsReceiverSoftware/include/UDPBaseImplementation.h index 6083b0723..60e07ef64 100644 --- a/slsReceiverSoftware/include/UDPBaseImplementation.h +++ b/slsReceiverSoftware/include/UDPBaseImplementation.h @@ -398,12 +398,13 @@ class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInter /** * Get the buffer-current frame read by receiver + * @param ithread port thread index * @param c pointer to current file name * @param raw address of pointer, pointing to current frame to send to gui * @param startAcq start index of the acquisition * @param startFrame start index of the scan */ - void readFrame(char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame); + void readFrame(int ithread, char* c,char** raw, int64_t &startAcq, int64_t &startFrame); /** * abort acquisition with minimum damage: close open files, cleanup. diff --git a/slsReceiverSoftware/include/UDPInterface.h b/slsReceiverSoftware/include/UDPInterface.h index dba7acf52..754aa5bd7 100644 --- a/slsReceiverSoftware/include/UDPInterface.h +++ b/slsReceiverSoftware/include/UDPInterface.h @@ -455,12 +455,13 @@ class UDPInterface { /** * Get the buffer-current frame read by receiver + * @param ithread port thread index * @param c pointer to current file name * @param raw address of pointer, pointing to current frame to send to gui * @param startAcq start index of the acquisition * @param startFrame start index of the scan */ - virtual void readFrame(char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame)=0; + virtual void readFrame(int ithread, char* c,char** raw, int64_t &startAcq, int64_t &startFrame)=0; /** * abort acquisition with minimum damage: close open files, cleanup. diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index fcec7fc0d..b17882baa 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -218,7 +218,7 @@ class UDPStandardImplementation: private virtual slsReceiverDefs, public UDPBase * @param startAcq start index of the acquisition * @param startFrame start index of the scan */ - void readFrame(int ithread, char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame); + void readFrame(int ithread, char* c,char** raw, int64_t &startAcq, int64_t &startFrame); /** * Overridden method @@ -571,8 +571,6 @@ private: /** Previous Frame number from buffer to calculate loss */ int64_t frameNumberInPreviousFile[MAX_NUMBER_OF_WRITER_THREADS]; - /** Last Frame Index Listened To */ - int64_t lastFrameIndex[MAX_NUMBER_OF_WRITER_THREADS]; @@ -586,7 +584,7 @@ private: int totalListeningPacketCount[MAX_NUMBER_OF_LISTENING_THREADS]; /** Pckets currently in current file, starts new file when it reaches max */ - uint64_t lastFrameNumberInFile[MAX_NUMBER_OF_WRITER_THREADS]; + int64_t lastFrameNumberInFile[MAX_NUMBER_OF_WRITER_THREADS]; /** packets in current file */ uint64_t totalPacketsInFile[MAX_NUMBER_OF_WRITER_THREADS]; diff --git a/slsReceiverSoftware/include/genericSocket.h b/slsReceiverSoftware/include/genericSocket.h index 0f52c0413..6cb9a8693 100644 --- a/slsReceiverSoftware/include/genericSocket.h +++ b/slsReceiverSoftware/include/genericSocket.h @@ -3,7 +3,7 @@ #define GENERIC_SOCKET_H - +#include "ansi.h" /** @@ -105,7 +105,8 @@ enum communicationProtocol{ packet_size(ps), nsending(0), nsent(0), - total_sent(0)// sender (client): where to? ip + total_sent(0),// sender (client): where to? ip + header_packet_size(0) { //memset(&serverAddress, 0, sizeof(sockaddr_in)); //memset(&clientAddress, 0, sizeof(sockaddr_in)); @@ -161,7 +162,7 @@ enum communicationProtocol{ */ - genericSocket(unsigned short int const port_number, communicationProtocol p, int ps = DEFAULT_PACKET_SIZE, const char *eth=NULL): + genericSocket(unsigned short int const port_number, communicationProtocol p, int ps = DEFAULT_PACKET_SIZE, const char *eth=NULL, int hsize=0): //portno(port_number), protocol(p), is_a_server(1), @@ -170,7 +171,8 @@ enum communicationProtocol{ packet_size(ps), nsending(0), nsent(0), - total_sent(0) + total_sent(0), + header_packet_size(hsize) { /* // you can specify an IP address: */ @@ -616,7 +618,8 @@ enum communicationProtocol{ nsent = recvfrom(socketDescriptor,(char*)buf+total_sent,nsending, 0, (struct sockaddr *) &clientAddress, &clientAddress_length); if(nsent < packet_size) { if(nsent){ - cout << "Incomplete Packet size " << nsent << endl; + if(nsent != header_packet_size) + cprintf(RED,"Incomplete Packet size %d\n",nsent); } break; } @@ -690,6 +693,11 @@ enum communicationProtocol{ } + + int getCurrentTotalReceived(){ + return total_sent; + } + char lastClientIP[INET_ADDRSTRLEN]; char thisClientIP[INET_ADDRSTRLEN]; int differentClients; @@ -712,7 +720,7 @@ enum communicationProtocol{ int nsending; int nsent; int total_sent; - + int header_packet_size; // pthread_mutex_t mp; diff --git a/slsReceiverSoftware/src/UDPBaseImplementation.cpp b/slsReceiverSoftware/src/UDPBaseImplementation.cpp index 8168affa4..858e968c2 100644 --- a/slsReceiverSoftware/src/UDPBaseImplementation.cpp +++ b/slsReceiverSoftware/src/UDPBaseImplementation.cpp @@ -431,7 +431,7 @@ int UDPBaseImplementation::shutDownUDPSockets(){ return OK; } -void UDPBaseImplementation::readFrame(char* c,char** raw, uint64_t &startAcquisitionIndex, uint64_t &startFrameIndex){ +void UDPBaseImplementation::readFrame(int ithread, char* c,char** raw, int64_t &startAcquisitionIndex, int64_t &startFrameIndex){ FILE_LOG(logWARNING) << __AT__ << " doing nothing..."; FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; } diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 0e9dc9015..db81cbd61 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -159,8 +159,7 @@ void UDPStandardImplementation::initializeMembers(){ frameIndex[i] = 0; currentFrameNumber[i] = 0; frameNumberInPreviousFile[i] = -1; - lastFrameIndex[i] = 0; - lastFrameNumberInFile[i] = 0; + lastFrameNumberInFile[i] = -1; totalPacketsInFile[i] = 0; } @@ -551,7 +550,6 @@ int UDPStandardImplementation::setDynamicRange(const uint32_t i){ //set parameters depending on new dynamic range. packetsPerFrame = (tengigaEnable ? EIGER_TEN_GIGA_CONSTANT : EIGER_ONE_GIGA_CONSTANT) * dynamicRange; bufferSize = onePacketSize * packetsPerFrame; - cout<<"packetsPerFrame:"<getCurrentTotalReceived(); + //wait for all packets if(totalP!=numberOfFrames*packetsPerFrame*numberofListeningThreads){ - prev = -1; - //wait as long as there is change from prev totalP - while(prev != totalP){ + //wait as long as there is change from prev totalP, + //and also change from received in buffer to previous value + //(as one listens to many at a time, shouldnt cut off in between) + while((prev != totalP) && (prevReceivedInBuffer!= currentReceivedInBuffer)){ #ifdef DEBUG5 - cprintf(MAGENTA,"waiting for all packets totalP:%d\n",totalP); + cprintf(MAGENTA,"waiting for all packets totalP:%d currently in buffer:%d\n",totalP,currentReceivedInBuffer); + #endif usleep(5000);/* Need to find optimal time (exposure time and acquisition period) **/ prev = totalP; - totalP=0; - for(i=0; igetCurrentTotalReceived(); } } @@ -1041,7 +1049,7 @@ void UDPStandardImplementation::startReadout(){ /**make this better by asking all of it at once*/ -void UDPStandardImplementation::readFrame(int ithread, char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame){ +void UDPStandardImplementation::readFrame(int ithread, char* c,char** raw, int64_t &startAcq, int64_t &startFrame){ FILE_LOG(logDEBUG) << __AT__ << " called"; //point to gui data, to let writer thread know that gui is back for data @@ -1311,20 +1319,23 @@ int UDPStandardImplementation::createUDPSockets(){ strcpy(eth,""); shutDownUDPSockets(); + int headerpacketsize = 0; + if(myDetectorType == EIGER) + headerpacketsize = EIGER_HEADER_LENGTH; //if no eth, listen to all if(!strlen(eth)){ FILE_LOG(logWARNING) << "eth is empty. Listening to all"; for(int i=0;iReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); - cout<ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); + totalListeningPacketCount[ithread] += (receivedSize/onePacketSize); #ifdef MANUALDEBUG @@ -1774,32 +1788,31 @@ void UDPStandardImplementation::stopListening(int ithread, int numbytes){ } //push dummy-end buffer into fifo for all writer threads - for(int i=0; ipop(buffer[ithread]); + fifoFree[ithread]->pop(buffer[ithread]); #ifdef EVERYFIFODEBUG - if(fifoFree[ithread]->getSemValue()<100) + if(fifoFree[ithread]->getSemValue()<100) cprintf(BLUE,"FifoFree[%d]: value:%d, pop 0x%x\n",ithread,fifoFree[ithread]->getSemValue(),(void*)(buffer[ithread])); #endif #ifdef CFIFODEBUG - if(ithread == 0) - cprintf(CYAN,"Listening_Thread %d: Popped Dummy from fifoFree %p\n", ithread,(void*)(buffer[ithread])); - else - cprintf(YELLOW,"Listening_Thread %d: Popped Dummy from fifoFree %p\n", ithread,(void*)(buffer[ithread])); + if(ithread == 0) + cprintf(CYAN,"Listening_Thread %d: Popped Dummy from fifoFree %p\n", ithread,(void*)(buffer[ithread])); + else + cprintf(YELLOW,"Listening_Thread %d: Popped Dummy from fifoFree %p\n", ithread,(void*)(buffer[ithread])); #endif - //creating dummy-end buffer with pc=0xFFFF - (*((uint32_t*)(buffer[ithread]))) = dummyPacketValue; - while(!fifo[ithread]->push(buffer[ithread])); + //creating dummy-end buffer with pc=0xFFFF + (*((uint32_t*)(buffer[ithread]))) = dummyPacketValue; + while(!fifo[ithread]->push(buffer[ithread])); #ifdef EVERYFIFODEBUG - if(fifo[ithread]->getSemValue()>(fifoSize-100)) + if(fifo[ithread]->getSemValue()>(fifoSize-100)) cprintf(MAGENTA,"Fifo[%d]: value:%d, push 0x%x\n",ithread,fifo[ithread]->getSemValue(),(void*)(buffer[ithread])); #endif #ifdef CFIFODEBUG - if(ithread == 0) - cprintf(CYAN,"Listening_Thread %d: Listener pushed dummy-end buffer into fifo %p\n", ithread,(void*)(buffer[ithread])); - else - cprintf(YELLOW,"Listening_Thread %d: Listener pushed dummy-end buffer into fifo %p\n", ithread,(void*)(buffer[ithread])); + if(ithread == 0) + cprintf(CYAN,"Listening_Thread %d: Listener pushed dummy-end buffer into fifo %p\n", ithread,(void*)(buffer[ithread])); + else + cprintf(YELLOW,"Listening_Thread %d: Listener pushed dummy-end buffer into fifo %p\n", ithread,(void*)(buffer[ithread])); #endif - } + //reset mask and exit loop @@ -2061,8 +2074,9 @@ void UDPStandardImplementation::waitWritingBufferForNextAcquisition(int ithread) //pop fifo so that its empty char* temp; while(!fifo[ithread]->isEmpty()){ - cprintf(RED,"%d:emptied buffer in fifo\n", ithread); + cprintf(RED,"%d:fifo emptied\n", ithread); fifo[ithread]->pop(temp); + fifoFree[ithread]->push(temp); #ifdef EVERYFIFODEBUG if(fifo[ithread]->getSemValue()>(fifoSize-100)) cprintf(CYAN,"Fifo[%d]: value:%d, pop 0x%x\n",ithread,fifo[ithread]->getSemValue(),(void*)(temp)); @@ -2087,7 +2101,6 @@ void UDPStandardImplementation::waitWritingBufferForNextAcquisition(int ithread) if(detindex == -1) sprintf(fileNamePerThread[ithread],"%s_d%d",fileName,ithread); - cout << "file name changed to include det Id:" << fileNamePerThread[ithread] << endl; } if(dataCompressionEnable){ @@ -2189,15 +2202,34 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ //statistics FILE_LOG(logINFO) << "Status: Run Finished"; - FILE_LOG(logINFO) << "Last Frame Number Caught:" << lastFrameIndex[ithread]; if(totalPacketsCaught < ((uint64_t)numberOfFrames*packetsPerFrame*numberofListeningThreads)){ cprintf(RED, "Total Missing Packets: %lld\n",(long long int)numberOfFrames*packetsPerFrame*numberofListeningThreads-totalPacketsCaught); cprintf(RED, "Total Packets Caught: %lld\n",(long long int)totalPacketsCaught); cprintf(RED, "Total Frames Caught: %lld\n",(long long int)(totalPacketsCaught/(packetsPerFrame*numberofListeningThreads))); + cprintf(RED, "Last Frame Number Caught: "); + int64_t lastFrameNumber = 0; + for(int i=0;i=0)){ //get start frame (required to create new file at the right juncture) uint64_t startframe =-1; if(getFrameNumber(ithread, wbuffer + offset, startframe) == FAIL){ @@ -2299,8 +2325,8 @@ cout<push(wbuffer)); return; } - cout<<"222"<=0) &&(!((lastFrameNumberInFile[ithread]+1) % maxFramesPerFile))){ createNewFile(ithread); } //frames to save in one file - nextFileFrameNumber = lastFrameNumberInFile[ithread] + - (maxFramesPerFile - (lastFrameNumberInFile[ithread]%maxFramesPerFile)); - if(!ithread) cout<push(wbuffer)); return; } - currentFrameNumber[ithread] = tempframenumber; + currentFrameNumber[ithread] = tempframenumber; + //set indices pthread_mutex_lock(&progressMutex); @@ -2648,14 +2669,15 @@ int UDPStandardImplementation::getFrameNumber(int ithread, char* wbuffer, uint64 FILE_LOG(logERROR) << "Fifo "<< ithread << ": Frame Number is zero from firmware."; return FAIL; } -//#ifdef DEBUG4 +#ifdef DEBUG4 if(!ithread) cprintf(GREEN,"Writing_Thread %d: fnum:%lld pnum:%d FPGA_fnum:%d footeroffset:%d\n", ithread, (long long int)tempframenumber, (*( (uint16_t*) footer->packetNumber)), (uint32_t)(*( (uint64_t*) footer)), footerOffset); -//#endif +#endif + tempframenumber += (startFrameIndex - 1); break; case JUNGFRAU: @@ -2666,6 +2688,7 @@ int UDPStandardImplementation::getFrameNumber(int ithread, char* wbuffer, uint64 (long long int)tempframenumber, (*( (uint8_t*) header->packetNumber))); #endif + tempframenumber += startFrameIndex; break; default: @@ -2680,7 +2703,7 @@ int UDPStandardImplementation::getFrameNumber(int ithread, char* wbuffer, uint64 (long long int)tempframenumber, pnum); #endif - + tempframenumber += startFrameIndex; break; } return OK; @@ -2694,119 +2717,103 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, FILE_LOG(logDEBUG) << __AT__ << " called"; - bool expectedoffsetATlastpacket = false; + int bigIncrements = onePacketSize * packetsPerFrame; //a frame at a time + if(numberofJobsPerBuffer == 1) bigIncrements = onePacketSize; //a packet at a time as we listen to only one frame in a buffer + int startoffset = offset; - if(!ithread) cout<= endoffset){ expectedoffset = startoffset + ((numpackets -1) * onePacketSize); expectedoffsetATlastpacket = true; } offset = expectedoffset; - if(!ithread) cout<push(wbuffer)); return FAIL; } - if(!ithread) cout<=nextFrameNumber){ - if(!ithread) cout<=nextFrameNumber){ - offset -= (onePacketSize*packetsPerFrame);/** its ok..if jonbsperthread is 1, go packet by packet*/ - if(!ithread) cout<push(wbuffer)); - return FAIL; - } - if(!ithread) cout<push(wbuffer)); return FAIL; } - if(!ithread) cout<push(wbuffer)); + return FAIL; + } + } + while(tempframenumberpush(wbuffer)); + return FAIL; + } + } } //if tempframenumber is too low, go forwards fast (by frame) and then slowly (by each packet) backwards else{ - if(!ithread) cout<endoffset){if(!ithread) cout<endoffset) + break; if(getFrameNumber(ithread, wbuffer + offset, tempframenumber) == FAIL){ //error in frame number sent by fpga while(!fifoFree[ithread]->push(wbuffer)); return FAIL; } - if(!ithread) cout<endoffset){ offset = endoffset; - if(!ithread) cout< end offset so offset now:"<nextFrameNumber){ - offset -= onePacketSize; - if(!ithread) cout<push(wbuffer)); return FAIL; } - if(!ithread) cout<nextFrameNumber){ + offset -= onePacketSize; + if(getFrameNumber(ithread, wbuffer + offset, tempframenumber) == FAIL){ + //error in frame number sent by fpga + while(!fifoFree[ithread]->push(wbuffer)); + return FAIL; + } } offset += onePacketSize; - if(!ithread) cout<getTotalFramesCaught(); - cout<<"frames caught sent:"<differentClients){ FILE_LOG(logDEBUG) << "Force update"; @@ -1132,8 +1131,8 @@ int slsReceiverTCPIPInterface::moench_read_frame(){ char* raw; - uint64_t startAcquisitionIndex=0; - uint64_t startFrameIndex=0; + int64_t startAcquisitionIndex=0; + int64_t startFrameIndex=0; uint32_t index = -1,bindex = 0, offset=0; strcpy(mess,"Could not read frame\n"); @@ -1153,7 +1152,7 @@ int slsReceiverTCPIPInterface::moench_read_frame(){ else{ ret = OK; - receiverBase->readFrame(fName,&raw,startAcquisitionIndex,startFrameIndex); + receiverBase->readFrame(0,fName,&raw,startAcquisitionIndex,startFrameIndex); /**send garbage with -1 index to try again*/ if (raw == NULL){ @@ -1321,8 +1320,8 @@ int slsReceiverTCPIPInterface::gotthard_read_frame(){ uint32_t index=-1,index2=0; uint32_t pindex=0,pindex2=0; uint32_t bindex=0,bindex2=0; - uint64_t startAcquisitionIndex=0; - uint64_t startFrameIndex=0; + int64_t startAcquisitionIndex=0; + int64_t startFrameIndex=0; strcpy(mess,"Could not read frame\n"); @@ -1341,7 +1340,7 @@ int slsReceiverTCPIPInterface::gotthard_read_frame(){ cout<<"haven't caught any frame yet"<readFrame(fName,&raw,startAcquisitionIndex,startFrameIndex); + receiverBase->readFrame(0,fName,&raw,startAcquisitionIndex,startFrameIndex); /**send garbage with -1 index to try again*/ if (raw == NULL){ @@ -1496,8 +1495,8 @@ int slsReceiverTCPIPInterface::propix_read_frame(){ uint32_t index=-1,index2=0; uint32_t pindex=0,pindex2=0; uint32_t bindex=0,bindex2=0; - uint64_t startAcquisitionIndex=0; - uint64_t startFrameIndex=0; + int64_t startAcquisitionIndex=0; + int64_t startFrameIndex=0; strcpy(mess,"Could not read frame\n"); @@ -1516,7 +1515,7 @@ int slsReceiverTCPIPInterface::propix_read_frame(){ cout<<"haven't caught any frame yet"<readFrame(fName,&raw,startAcquisitionIndex,startFrameIndex); + receiverBase->readFrame(0,fName,&raw,startAcquisitionIndex,startFrameIndex); /**send garbage with -1 index to try again*/ if (raw == NULL){ @@ -1634,22 +1633,27 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ char fName[MAX_STR_LENGTH]=""; int acquisitionIndex = -1; int frameIndex= -1; - uint32_t index=0; - uint32_t subframenumber=-1; + int index=0; + int subframenumber=-1; - int frameSize = EIGER_ONE_GIGA_ONE_PACKET_SIZE * packetsPerFrame; - int dataSize = EIGER_ONE_GIGA_ONE_DATA_SIZE * packetsPerFrame; + int frameSize = EIGER_ONE_GIGA_ONE_PACKET_SIZE * packetsPerFrame * EIGER_MAX_PORTS; + int dataSize = EIGER_ONE_GIGA_ONE_DATA_SIZE * packetsPerFrame * EIGER_MAX_PORTS; int oneDataSize = EIGER_ONE_GIGA_ONE_DATA_SIZE; + int onePacketSize = EIGER_ONE_GIGA_ONE_PACKET_SIZE; if(tenGigaEnable){ - frameSize = EIGER_TEN_GIGA_ONE_PACKET_SIZE * packetsPerFrame; - dataSize = EIGER_TEN_GIGA_ONE_DATA_SIZE * packetsPerFrame; + frameSize = EIGER_TEN_GIGA_ONE_PACKET_SIZE * packetsPerFrame * EIGER_MAX_PORTS; + dataSize = EIGER_TEN_GIGA_ONE_DATA_SIZE * packetsPerFrame * EIGER_MAX_PORTS; oneDataSize = EIGER_TEN_GIGA_ONE_DATA_SIZE; + onePacketSize = EIGER_TEN_GIGA_ONE_PACKET_SIZE; } char* raw; char* origVal = new char[frameSize]; char* retval = new char[dataSize]; - uint64_t startAcquisitionIndex=0; - uint64_t startFrameIndex=0; + memset(origVal,0xF,frameSize); + memset(retval,0xF,dataSize); + + int64_t startAcquisitionIndex=0; + int64_t startFrameIndex=0; strcpy(mess,"Could not read frame\n"); @@ -1674,35 +1678,40 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ else{ ret = OK; //read a frame - receiverBase->readFrame(fName,&raw,startAcquisitionIndex,startFrameIndex); - //send garbage with -1 index to try again - if (raw == NULL){ - startAcquisitionIndex = -1; + for(int i=0;ireadFrame(i,fName,&raw,startAcquisitionIndex,startFrameIndex); + //send garbage with -1 index to try again + if (raw == NULL){ + startAcquisitionIndex = -1; #ifdef VERYVERBOSE - cout<<"data not ready for gui yet"<subFrameNumber); } -#ifdef VERYVERBOSE +//#ifdef VERYVERBOSE cout << "index:" << dec << index << endl; cout << "subframenumber:" << dec << subframenumber << endl; -#endif +//#endif + - memcpy(origVal,raw,frameSize); - raw=NULL; int c1=8;//first port int c2=(frameSize/2) + 8; //second port @@ -1815,18 +1824,18 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ startFrameIndex = -1; else frameIndex = index-startFrameIndex; -#ifdef VERY_VERY_DEBUG +//#ifdef VERY_VERY_DEBUG cout << "acquisitionIndex calculated is:" << acquisitionIndex << endl; cout << "frameIndex calculated is:" << frameIndex << endl; cout << "index:" << index << endl; cout << "startAcquisitionIndex:" << startAcquisitionIndex << endl; cout << "startFrameIndex:" << startFrameIndex << endl; cout << "subframenumber:" << subframenumber << endl; -#endif +//#endif } } -#ifdef VERYVERBOSE +//#ifdef VERYVERBOSE if(frameIndex!=-1){ cout << "fName:" << fName << endl; cout << "acquisitionIndex:" << acquisitionIndex << endl; @@ -1835,7 +1844,7 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ cout << "startFrameIndex:" << startFrameIndex << endl; cout << "subframenumber:" << subframenumber << endl; } -#endif +//#endif @@ -1880,8 +1889,8 @@ int slsReceiverTCPIPInterface::jungfrau_read_frame(){ int acquisitionIndex = -1; int frameIndex= -1; int64_t currentIndex=0; - uint64_t startAcquisitionIndex=0; - uint64_t startFrameIndex=0; + int64_t startAcquisitionIndex=0; + int64_t startFrameIndex=0; strcpy(mess,"Could not read frame\n"); @@ -1919,7 +1928,7 @@ int slsReceiverTCPIPInterface::jungfrau_read_frame(){ else{ ret = OK; //read a frame - receiverBase->readFrame(fName,&raw,startAcquisitionIndex,startFrameIndex); + receiverBase->readFrame(0,fName,&raw,startAcquisitionIndex,startFrameIndex); //send garbage with -1 index to try again if (raw == NULL){ startAcquisitionIndex = -1; From eea2809136e1c1e5c0d9fdc9d06a1b86d5a89a89 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Thu, 1 Sep 2016 11:22:28 +0200 Subject: [PATCH 13/48] in between --- .../src/UDPStandardImplementation.cpp | 36 ++++++++---------- .../src/slsReceiverTCPIPInterface.cpp | 38 ++++++++++--------- 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index db81cbd61..df0b7ccf9 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -727,7 +727,7 @@ int UDPStandardImplementation::setDetectorType(const detectorType d){ frameIndexMask = EIGER_FRAME_INDEX_MASK; frameIndexOffset = EIGER_FRAME_INDEX_OFFSET; packetIndexMask = EIGER_PACKET_INDEX_MASK; - maxFramesPerFile = 5;//EIGER_MAX_FRAMES_PER_FILE; + maxFramesPerFile = EIGER_MAX_FRAMES_PER_FILE; fifoSize = EIGER_FIFO_SIZE; fifoDepth = EIGER_FIFO_SIZE; footerOffset = EIGER_PACKET_HEADER_SIZE + oneDataSize; @@ -1655,7 +1655,7 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, ch if(status != TRANSMITTING) receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); //eiger returns 0 when header packet caught - if(!receivedSize && status != TRANSMITTING) + while(receivedSize < onePacketSize && status != TRANSMITTING) receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); totalListeningPacketCount[ithread] += (receivedSize/onePacketSize); @@ -2206,28 +2206,24 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ cprintf(RED, "Total Missing Packets: %lld\n",(long long int)numberOfFrames*packetsPerFrame*numberofListeningThreads-totalPacketsCaught); cprintf(RED, "Total Packets Caught: %lld\n",(long long int)totalPacketsCaught); cprintf(RED, "Total Frames Caught: %lld\n",(long long int)(totalPacketsCaught/(packetsPerFrame*numberofListeningThreads))); - cprintf(RED, "Last Frame Number Caught: "); int64_t lastFrameNumber = 0; for(int i=0;i=0)){ //get start frame (required to create new file at the right juncture) uint64_t startframe =-1; + //cout<<"getting start frame number"<push(wbuffer)); return; } - + //cout<<"done getting start frame number"<push(wbuffer)); @@ -2752,7 +2748,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, while(tempframenumber>=nextFrameNumber){ offset -= bigIncrements; if(offsetpush(wbuffer)); @@ -2760,7 +2756,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } } if(offsetpush(wbuffer)); @@ -2768,7 +2764,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } } while(tempframenumberpush(wbuffer)); @@ -2782,7 +2778,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, while(tempframenumberendoffset) - break; + break;//cout<<"frame number at going forwards fast"<push(wbuffer)); @@ -2790,7 +2786,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } } if(offset>endoffset){ - offset = endoffset; + offset = endoffset;//cout<<"frame number at offset>endoffset"<push(wbuffer)); @@ -2798,7 +2794,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } } while(tempframenumber>nextFrameNumber){ - offset -= onePacketSize; + offset -= onePacketSize;//cout<<"frame number at going bacckwards slow"<push(wbuffer)); @@ -2814,7 +2810,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, fwrite(wbuffer + startoffset, 1, offset-startoffset, sfilefd[ithread]); numPacketsWritten += ((offset-startoffset)/onePacketSize); lastFrameNumberInFile[ithread] = frameNumberWritten; - + //cout<<"done with writeUptoFrameNumber" << endl; return OK; } diff --git a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp index 6c8092c4c..abb1308e8 100644 --- a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp +++ b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp @@ -1685,9 +1685,25 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ startAcquisitionIndex = -1; #ifdef VERYVERBOSE cout<<"data not ready for gui yet"<subFrameNumber); + } +#ifdef VERYVERBOSE + cout << "index:" << dec << index << endl; + if(index>10000) exit(-1); + cout << "subframenumber:" << dec << subframenumber << endl; +#endif + memcpy(((char*)origVal)+(i*onePacketSize*packetsPerFrame),raw,(frameSize/EIGER_MAX_PORTS)); raw=NULL; } @@ -1696,20 +1712,6 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ if(startAcquisitionIndex != -1){ //cout<<"**** got proper frame ******"<subFrameNumber); - } - -//#ifdef VERYVERBOSE - cout << "index:" << dec << index << endl; - cout << "subframenumber:" << dec << subframenumber << endl; -//#endif @@ -1824,18 +1826,18 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ startFrameIndex = -1; else frameIndex = index-startFrameIndex; -//#ifdef VERY_VERY_DEBUG +#ifdef VERY_VERY_DEBUG cout << "acquisitionIndex calculated is:" << acquisitionIndex << endl; cout << "frameIndex calculated is:" << frameIndex << endl; cout << "index:" << index << endl; cout << "startAcquisitionIndex:" << startAcquisitionIndex << endl; cout << "startFrameIndex:" << startFrameIndex << endl; cout << "subframenumber:" << subframenumber << endl; -//#endif +#endif } } -//#ifdef VERYVERBOSE +#ifdef VERYVERBOSE if(frameIndex!=-1){ cout << "fName:" << fName << endl; cout << "acquisitionIndex:" << acquisitionIndex << endl; @@ -1844,7 +1846,7 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ cout << "startFrameIndex:" << startFrameIndex << endl; cout << "subframenumber:" << subframenumber << endl; } -//#endif +#endif From 5b3ab9a2b4e3ab8c8d6889862d4a2271c5396bd2 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Thu, 1 Sep 2016 13:34:02 +0200 Subject: [PATCH 14/48] soewhere --- .../src/UDPStandardImplementation.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index df0b7ccf9..4ba857b2c 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1660,7 +1660,7 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, ch totalListeningPacketCount[ithread] += (receivedSize/onePacketSize); -#ifdef MANUALDEBUG +//#ifdef MANUALDEBUG if(receivedSize>0){ if(myDetectorType == JUNGFRAU){ jfrau_packet_header_t* header; @@ -1681,7 +1681,7 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, ch (uint32_t)(*( (uint64_t*) footer))); } } -#endif +//#endif #ifdef DEBUG cprintf(BLUE, "Listening_Thread %d : Received bytes: %d. Expected bytes: %d\n", ithread, receivedSize, bufferSize * numberofJobsPerBuffer-cSize); #endif @@ -2309,20 +2309,20 @@ void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* w int offset = HEADER_SIZE_NUM_TOT_PACKETS; uint64_t nextFileFrameNumber; int packetsWritten = 0; - + //if(ithread) cout<<"numpackets:"<=0)){ //get start frame (required to create new file at the right juncture) uint64_t startframe =-1; - //cout<<"getting start frame number"<push(wbuffer)); return; } - //cout<<"done getting start frame number"<push(wbuffer)); @@ -2748,7 +2748,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, while(tempframenumber>=nextFrameNumber){ offset -= bigIncrements; if(offsetpush(wbuffer)); @@ -2756,7 +2756,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } } if(offsetpush(wbuffer)); @@ -2764,7 +2764,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } } while(tempframenumberpush(wbuffer)); @@ -2778,7 +2778,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, while(tempframenumberendoffset) - break;//cout<<"frame number at going forwards fast"<push(wbuffer)); @@ -2786,7 +2786,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } } if(offset>endoffset){ - offset = endoffset;//cout<<"frame number at offset>endoffset"<endoffset f#:"<push(wbuffer)); @@ -2794,7 +2794,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } } while(tempframenumber>nextFrameNumber){ - offset -= onePacketSize;//cout<<"frame number at going bacckwards slow"<push(wbuffer)); @@ -2810,7 +2810,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, fwrite(wbuffer + startoffset, 1, offset-startoffset, sfilefd[ithread]); numPacketsWritten += ((offset-startoffset)/onePacketSize); lastFrameNumberInFile[ithread] = frameNumberWritten; - //cout<<"done with writeUptoFrameNumber" << endl; + //if(ithread) cout<<"done with writeUptoFrameNumber" << endl; return OK; } From 4eceb3b5f76ec26140d22a831c375822cf32d240 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Fri, 2 Sep 2016 15:47:28 +0200 Subject: [PATCH 15/48] kinda --- .../include/UDPStandardImplementation.h | 8 +- .../src/UDPBaseImplementation.cpp | 1 + .../src/UDPStandardImplementation.cpp | 221 +++++++++--------- .../src/slsReceiverTCPIPInterface.cpp | 36 +-- 4 files changed, 139 insertions(+), 127 deletions(-) diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index b17882baa..b043eeedf 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -220,6 +220,8 @@ class UDPStandardImplementation: private virtual slsReceiverDefs, public UDPBase */ void readFrame(int ithread, char* c,char** raw, int64_t &startAcq, int64_t &startFrame); + + void resetGuiPointer(int ithread); /** * Overridden method * Closes file / all files(data compression involves multiple files) @@ -456,8 +458,9 @@ private: * Uses semaphore for nth frame mode * @param ithread writer thread index * @param buffer buffer to copy + * @param numpackets number of packets to copy */ - void copyFrameToGui(int ithread, char* buffer); + void copyFrameToGui(int ithread, char* buffer, uint32_t numpackets); void waitWritingBufferForNextAcquisition(int ithread); @@ -589,6 +592,9 @@ private: /** packets in current file */ uint64_t totalPacketsInFile[MAX_NUMBER_OF_WRITER_THREADS]; + /**Total packet count written by each writing thread */ + uint64_t totalWritingPacketCount[MAX_NUMBER_OF_LISTENING_THREADS]; + diff --git a/slsReceiverSoftware/src/UDPBaseImplementation.cpp b/slsReceiverSoftware/src/UDPBaseImplementation.cpp index 858e968c2..d4bbbcfbd 100644 --- a/slsReceiverSoftware/src/UDPBaseImplementation.cpp +++ b/slsReceiverSoftware/src/UDPBaseImplementation.cpp @@ -436,6 +436,7 @@ void UDPBaseImplementation::readFrame(int ithread, char* c,char** raw, int64_t & FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; } + //FIXME: needed, isnt stopReceiver enough? void UDPBaseImplementation::abort(){ FILE_LOG(logWARNING) << __AT__ << " doing nothing..."; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 4ba857b2c..7f649d5c3 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -161,6 +161,7 @@ void UDPStandardImplementation::initializeMembers(){ frameNumberInPreviousFile[i] = -1; lastFrameNumberInFile[i] = -1; totalPacketsInFile[i] = 0; + totalWritingPacketCount[i] = 0; } @@ -849,6 +850,7 @@ int UDPStandardImplementation::startReceiver(char *c){ //reset file parameters lastFrameNumberInFile[i] = -1; totalPacketsInFile[i] = 0; + totalWritingPacketCount[i] = 0; if(sfilefd[i]){ fclose(sfilefd[i]); sfilefd[i] = NULL; @@ -1015,7 +1017,7 @@ void UDPStandardImplementation::startReadout(){ //wait as long as there is change from prev totalP, //and also change from received in buffer to previous value //(as one listens to many at a time, shouldnt cut off in between) - while((prev != totalP) && (prevReceivedInBuffer!= currentReceivedInBuffer)){ + while((prev != totalP) || (prevReceivedInBuffer!= currentReceivedInBuffer)){ #ifdef DEBUG5 cprintf(MAGENTA,"waiting for all packets totalP:%d currently in buffer:%d\n",totalP,currentReceivedInBuffer); @@ -1031,7 +1033,12 @@ void UDPStandardImplementation::startReadout(){ currentReceivedInBuffer = 0; for(i=0; igetCurrentTotalReceived(); +#ifdef DEBUG5 + cprintf(MAGENTA,"\tupdated: totalP:%d currently in buffer:%d\n",totalP,currentReceivedInBuffer); + +#endif } + } //set status @@ -1080,6 +1087,7 @@ void UDPStandardImplementation::readFrame(int ithread, char* c,char** raw, int64 cprintf(CYAN,"Info: gui data ready\n"); #endif *raw = guiData[ithread]; + guiData[ithread] = NULL; //for nth frame to gui, post semaphore so writer stops waiting @@ -1097,7 +1105,25 @@ void UDPStandardImplementation::readFrame(int ithread, char* c,char** raw, int64 } } +/* +void UDPStandardImplementation::resetGuiPointer(int ithread){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + guiData[ithread] = NULL; + + //for nth frame to gui, post semaphore so writer stops waiting + if((FrameToGuiFrequency) && (writerThreadsMask)){ +#ifdef DEBUG4 + cprintf(CYAN,"Info: gonna post\n"); +#endif + //release after getting data + sem_post(&writerGuiSemaphore[ithread]); + } +#ifdef DEBUG4 + cprintf(CYAN,"Info: done post\n"); +#endif +} +*/ void UDPStandardImplementation::closeFile(int ithread){ FILE_LOG(logDEBUG) << __AT__ << " called for " << ithread ; @@ -1408,7 +1434,7 @@ int UDPStandardImplementation::createNewFile(int ithread){ FILE_LOG(logDEBUG) << __AT__ << " called"; int index = 0; - if(packetsCaught) + if(totalWritingPacketCount[ithread]) index = frameIndex[ithread]; //create file name @@ -1445,7 +1471,7 @@ int UDPStandardImplementation::createNewFile(int ithread){ //Print packet loss and filenames - if(!packetsCaught){ + if(!totalWritingPacketCount[ithread]){ frameNumberInPreviousFile[ithread] = -1; cout << "Thread " << ithread << " File:" << completeFileName[ithread] << endl; }else{ @@ -1458,7 +1484,8 @@ int UDPStandardImplementation::createNewFile(int ithread){ << "\tPacket Loss: " << setw(4)<0){ if(myDetectorType == JUNGFRAU){ jfrau_packet_header_t* header; @@ -1681,7 +1708,7 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, ch (uint32_t)(*( (uint64_t*) footer))); } } -//#endif +#endif #ifdef DEBUG cprintf(BLUE, "Listening_Thread %d : Received bytes: %d. Expected bytes: %d\n", ithread, receivedSize, bufferSize * numberofJobsPerBuffer-cSize); #endif @@ -2202,31 +2229,33 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ //statistics FILE_LOG(logINFO) << "Status: Run Finished"; - if(totalPacketsCaught < ((uint64_t)numberOfFrames*packetsPerFrame*numberofListeningThreads)){ - cprintf(RED, "Total Missing Packets: %lld\n",(long long int)numberOfFrames*packetsPerFrame*numberofListeningThreads-totalPacketsCaught); - cprintf(RED, "Total Packets Caught: %lld\n",(long long int)totalPacketsCaught); - cprintf(RED, "Total Frames Caught: %lld\n",(long long int)(totalPacketsCaught/(packetsPerFrame*numberofListeningThreads))); - int64_t lastFrameNumber = 0; - for(int i=0;i= packetsPerFrame)/**needs to be reworked*/ - copyFrameToGui(ithread, wbuffer); + //if(npackets >= (packetsPerFrame/numberofListeningThreads)) + if(npackets) + copyFrameToGui(ithread, wbuffer,npackets); #ifdef DEBUG4 cprintf(GREEN,"Writing_Thread: Copied frame\n"); #endif @@ -2331,6 +2361,7 @@ void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* w //update stats numpackets -= packetsWritten; totalPacketsInFile[ithread] += packetsWritten; + totalWritingPacketCount[ithread] += packetsWritten; pthread_mutex_lock(&writeMutex); packetsCaught += packetsWritten; totalPacketsCaught += packetsWritten; @@ -2342,9 +2373,9 @@ void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* w while(numpackets){ //new file //create new file only if something has been written and modulus works - if((lastFrameNumberInFile[ithread]>=0) &&(!((lastFrameNumberInFile[ithread]+1) % maxFramesPerFile))){ + if((lastFrameNumberInFile[ithread]>=0) &&(!((lastFrameNumberInFile[ithread]+1) % maxFramesPerFile))) createNewFile(ithread); - } + //frames to save in one file nextFileFrameNumber = (lastFrameNumberInFile[ithread]+1) + @@ -2357,6 +2388,7 @@ void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* w //update stats numpackets -= packetsWritten; totalPacketsInFile[ithread] += packetsWritten; + totalWritingPacketCount[ithread] += packetsWritten; pthread_mutex_lock(&writeMutex); packetsCaught += packetsWritten; totalPacketsCaught += packetsWritten; @@ -2378,8 +2410,10 @@ void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* w return; } totalPacketsInFile[ithread] += numpackets; + totalWritingPacketCount[ithread] += numpackets; lastFrameNumberInFile[ithread] = finalLastFrameNumberToSave; currentFrameNumber[ithread] = finalLastFrameNumberToSave; + } if(numberofWriterThreads > 1) pthread_mutex_lock(&writeMutex); @@ -2447,7 +2481,7 @@ void UDPStandardImplementation::updateFileHeader(int ithread){ } -void UDPStandardImplementation::copyFrameToGui(int ithread, char* buffer){ +void UDPStandardImplementation::copyFrameToGui(int ithread, char* buffer, uint32_t numpackets){ FILE_LOG(logDEBUG) << __AT__ << " called"; @@ -2475,7 +2509,7 @@ void UDPStandardImplementation::copyFrameToGui(int ithread, char* buffer){ #ifdef DEBUG4 cprintf(GREEN,"Writing_Thread: CopyingFrame: guidataready is 0, Copying data\n"); #endif - memcpy(latestData[ithread],buffer + HEADER_SIZE_NUM_TOT_PACKETS,bufferSize); + memcpy(latestData[ithread],buffer , numpackets*onePacketSize); strcpy(guiFileName[ithread],completeFileName[ithread]); guiDataReady[ithread]=1; pthread_mutex_unlock(&dataReadyMutex); @@ -2612,6 +2646,7 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer #ifndef ALLFILE totalPacketsInFile[ithread] += (bufferSize/packetsPerFrame); + totalWritingPacketCount[ithread] += (bufferSize/packetsPerFrame); pthread_mutex_lock(&writeMutex); if((packetsCaught%packetsPerFrame) >= (uint32_t)maxFramesPerFile) createNewFile(ithread); @@ -2622,7 +2657,7 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer #endif if(!once){ - copyFrameToGui(ithread, buff[0]); + copyFrameToGui(ithread, buff[0],(uint32_t)packetsPerFrame); once = 1; } } @@ -2662,7 +2697,6 @@ int UDPStandardImplementation::getFrameNumber(int ithread, char* wbuffer, uint64 if(!((uint32_t)(*( (uint64_t*) footer)))){ tempframenumber = -1; FILE_LOG(logERROR) << "Fifo "<< ithread << ": Frame Number is zero from firmware."; - exit(-1); return FAIL; } #ifdef DEBUG4 @@ -2713,103 +2747,66 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, FILE_LOG(logDEBUG) << __AT__ << " called"; //if(ithread) cout<<"at writeUptoFrameNumber " << nextFrameNumber<< endl; - int bigIncrements = onePacketSize * packetsPerFrame; //a frame at a time - if(numberofJobsPerBuffer == 1) bigIncrements = onePacketSize; //a packet at a time as we listen to only one frame in a buffer int startoffset = offset; int endoffset = startoffset + numpackets * onePacketSize; - - int expectedoffset = startoffset + ((nextFrameNumber - (lastFrameNumberInFile[ithread]+1)) * onePacketSize * packetsPerFrame); - bool expectedoffsetATlastpacket = false; - if(expectedoffset >= endoffset){ - expectedoffset = startoffset + ((numpackets -1) * onePacketSize); - expectedoffsetATlastpacket = true; - } - offset = expectedoffset; - - - //get frame number at expected offset uint64_t tempframenumber=-1; - uint64_t frameNumberWritten=-1;//if(ithread) cout<<"frame number at expected ofset"<push(wbuffer)); return FAIL; } + //last packet's frame number < nextframenumber + if(tempframenumber=nextFrameNumber){ - while(tempframenumber>=nextFrameNumber){ - offset -= bigIncrements; - if(offsetpush(wbuffer)); - return FAIL; - } - } - if(offsetpush(wbuffer)); - return FAIL; - } - } - while(tempframenumberpush(wbuffer)); - return FAIL; - } - } + + //somewhere in between + int bigIncrements = onePacketSize * packetsPerFrame * 10; //10 frames at a time + if(numberofJobsPerBuffer == 1) bigIncrements = onePacketSize; //a packet at a time as we listen to only one frame in a buffer + + cout<=nextFrameNumber){ + offset -= bigIncrements; + if(offsetpush(wbuffer)); + return FAIL; } - - //if tempframenumber is too low, go forwards fast (by frame) and then slowly (by each packet) backwards - else{ - while(tempframenumberendoffset) - break;//if(ithread) cout<<"frame number at going forwards fast f#:"<push(wbuffer)); - return FAIL; - } - } - if(offset>endoffset){ - offset = endoffset;//if(ithread) cout<<"frame number at offset>endoffset f#:"<push(wbuffer)); - return FAIL; - } - } - while(tempframenumber>nextFrameNumber){ - offset -= onePacketSize;//if(ithread) cout<<"frame number at going bacckwards slow f#:"<push(wbuffer)); - return FAIL; - } - } - offset += onePacketSize; + } + if(offsetpush(wbuffer)); + return FAIL; + } + } + while(tempframenumberpush(wbuffer)); + return FAIL; } - frameNumberWritten = nextFrameNumber-1; } fwrite(wbuffer + startoffset, 1, offset-startoffset, sfilefd[ithread]); numPacketsWritten += ((offset-startoffset)/onePacketSize); - lastFrameNumberInFile[ithread] = frameNumberWritten; + lastFrameNumberInFile[ithread] = (nextFrameNumber-1); //if(ithread) cout<<"done with writeUptoFrameNumber" << endl; return OK; } diff --git a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp index abb1308e8..500db2988 100644 --- a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp +++ b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp @@ -1164,7 +1164,7 @@ int slsReceiverTCPIPInterface::moench_read_frame(){ else{ bindex = ((uint32_t)(*((uint32_t*)raw))); - memcpy(origVal,raw,bufferSize); + memcpy(origVal,raw + HEADER_SIZE_NUM_TOT_PACKETS,bufferSize); raw=NULL; //************** packet number order********************** @@ -1369,7 +1369,7 @@ int slsReceiverTCPIPInterface::gotthard_read_frame(){ #endif } - memcpy(origVal,raw,bufferSize); + memcpy(origVal,raw + HEADER_SIZE_NUM_TOT_PACKETS,bufferSize); raw=NULL; @@ -1535,7 +1535,7 @@ int slsReceiverTCPIPInterface::propix_read_frame(){ cout << "index2:" << hex << index << endl; #endif - memcpy(origVal,raw,bufferSize); + memcpy(origVal,raw + HEADER_SIZE_NUM_TOT_PACKETS,bufferSize); raw=NULL; /*//ignore if half frame is missing @@ -1649,8 +1649,8 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ char* raw; char* origVal = new char[frameSize]; char* retval = new char[dataSize]; - memset(origVal,0xF,frameSize); - memset(retval,0xF,dataSize); + memset(origVal,0xFF,frameSize); + memset(retval,0xFF,dataSize); int64_t startAcquisitionIndex=0; int64_t startFrameIndex=0; @@ -1677,6 +1677,9 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ // acq started else{ ret = OK; + int fnum[EIGER_MAX_PORTS]; + for(int i=0;ireadFrame(i,fName,&raw,startAcquisitionIndex,startFrameIndex); @@ -1684,27 +1687,28 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ if (raw == NULL){ startAcquisitionIndex = -1; #ifdef VERYVERBOSE - cout<<"data not ready for gui yet"<subFrameNumber); } #ifdef VERYVERBOSE cout << "index:" << dec << index << endl; - if(index>10000) exit(-1); cout << "subframenumber:" << dec << subframenumber << endl; #endif - - memcpy(((char*)origVal)+(i*onePacketSize*packetsPerFrame),raw,(frameSize/EIGER_MAX_PORTS)); + int numpackets = (uint32_t)(*( (uint32_t*) raw)); + memcpy(((char*)origVal)+(i*onePacketSize*packetsPerFrame),raw + HEADER_SIZE_NUM_TOT_PACKETS,numpackets*onePacketSize); raw=NULL; } } @@ -1712,8 +1716,12 @@ int slsReceiverTCPIPInterface::eiger_read_frame(){ if(startAcquisitionIndex != -1){ //cout<<"**** got proper frame ******"<resetGuiPointer(i); - + if(fnum[0]!=fnum[1]) + cprintf(BG_RED,"Fnums differ %d and %d\n",fnum[0],fnum[1]); int c1=8;//first port int c2=(frameSize/2) + 8; //second port @@ -1942,7 +1950,7 @@ int slsReceiverTCPIPInterface::jungfrau_read_frame(){ //proper frame else{ //cout<<"**** got proper frame ******"< Date: Mon, 5 Sep 2016 10:09:49 +0200 Subject: [PATCH 16/48] in between --- .../include/UDPStandardImplementation.h | 41 ++++- .../src/UDPStandardImplementation.cpp | 166 ++++++++++++++++-- 2 files changed, 190 insertions(+), 17 deletions(-) diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index b043eeedf..6674c62d9 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -289,6 +289,12 @@ private: /************************************************************************* * Listening and Writing Threads ***************************************** *************************************************************************/ + /** + * Create Data Call Back Threads + * @param destroy is true to destroy all the threads + * @return OK or FAIL + */ + int createDataCallbackThreads(bool destroy = false); /** * Create Listening Threads @@ -303,6 +309,9 @@ private: */ int createWriterThreads(bool destroy = false); + + + /** * Set Thread Priorities */ @@ -336,6 +345,12 @@ private: */ int createCompressionFile(int ithread, int iframe); + /** + * Static function - Starts Data Callback Thread of this object + * @param this_pointer pointer to this object + */ + static void* startDataCallbackThread(void *this_pointer); + /** * Static function - Starts Listening Thread of this object * @param this_pointer pointer to this object @@ -348,6 +363,11 @@ private: */ static void* startWritingThread(void *this_pointer); + /** + * Thread that sends data packets to client + */ + void startDataCallback(); + /** * Thread that listens to packets * It pops the fifofree for free addresses, listens to packets and pushes them into the fifo @@ -652,6 +672,24 @@ private: + //***data call back thread parameters*** + /** Number of data callback Threads */ + int numberofDataCallbackThreads; + + /** Data Callback Threads */ + pthread_t dataCallbackThreads[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** Semaphores Synchronizing DataCallback Threads */ + sem_t dataCallbackSemaphore[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** Mask with each bit indicating status of each data callback thread */ + volatile uint32_t dataCallbackThreadsMask; + + /** Set to self-terminate data callback threads waiting for semaphores */ + bool killAllDataCallbackThreads; + + bool dataCallbackEnabled; + //***general and listening thread parameters*** /** Ensures if threads created successfully */ @@ -669,9 +707,6 @@ private: /** Semaphores Synchronizing Listening Threads */ sem_t listenSemaphore[MAX_NUMBER_OF_LISTENING_THREADS]; - /** Current Listening Thread Index*/ - int currentListeningThreadIndex; - /** Mask with each bit indicating status of each listening thread */ volatile uint32_t listeningThreadsMask; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 7f649d5c3..983fd1fb8 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -186,6 +186,12 @@ void UDPStandardImplementation::initializeMembers(){ frametoGuiCounter[i] = 0; } + //***data callback thread parameters*** + numberofDataCallbackThreads = 1; + dataCallbackThreadsMask = 0x0; + killAllDataCallbackThreads = false; + dataCallbackEnabled = true; /**false*/ + //***general and listening thread parameters*** threadStarted = false; currentThreadIndex = -1; @@ -767,14 +773,17 @@ int UDPStandardImplementation::setDetectorType(const detectorType d){ //delete threads and set number of listening threads if(myDetectorType == EIGER){ pthread_mutex_lock(&statusMutex); + dataCallbackThreadsMask = 0x0; listeningThreadsMask = 0x0; writerThreadsMask = 0x0; pthread_mutex_unlock(&(statusMutex)); if(threadStarted){ createListeningThreads(true); + createDataCallbackThreads(true); createWriterThreads(true); } numberofListeningThreads = MAX_NUMBER_OF_LISTENING_THREADS; + numberofDataCallbackThreads = MAX_NUMBER_OF_LISTENING_THREADS; numberofWriterThreads = MAX_NUMBER_OF_WRITER_THREADS; } @@ -793,7 +802,8 @@ int UDPStandardImplementation::setDetectorType(const detectorType d){ //updates File Header if(myDetectorType == EIGER){ for(int i=0; istartDataCallback(); + return this_pointer; +} + + + void* UDPStandardImplementation::startListeningThread(void* this_pointer){ FILE_LOG(logDEBUG) << __AT__ << " called"; ((UDPStandardImplementation*)this_pointer)->startListening(); @@ -1560,6 +1643,62 @@ void* UDPStandardImplementation::startWritingThread(void* this_pointer){ +void UDPStandardImplementation::startDataCallback(){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + //set current thread value index + int ithread = currentThreadIndex; + //let calling function know thread started and obtained current + threadStarted = 1; + char* buffer; + // server address to bind + const char *hostName = "tcp://127.0.0.1:70001";/**increment this by ithread and detid*/ + + /* outer loop - loops once for each acquisition */ + //infinite loop, exited only to change dynamic range, 10G parameters etc (then recreated again) + while(true){ + + void *context = zmq_ctx_new(); + // create a publisher + socket = zmq_socket(context, ZMQ_PUB); + // bind + zmq_bind(socket,hostName);/**increment this by 1*/ + + /* inner loop - loop for each buffer */ + //until mask reset (udp sockets shut down by client) + while((1 << ithread) & dataCallbackThreadsMask){ + + //wait for data + sem_wait(&dataCallbackSemaphore[ithread]); + if(status == TRANSMITTING) + continue; + int numpackets = (uint32_t)(*( (uint32_t*) latestData)); /*latestdata should be size of one buffer*/ + memcpy(buffer, latestData, numpackets*onePacketSize);/**read first bytes to get numpackets*/ + + /*check if it should be added to previous (processlistening buffer for datacompression)*/ + + zmq_send(socket, buffer.data(), buffer.size(), 0); + + }/*--end of loop for each buffer (inner loop)*/ + + //end of acquisition, wait for next acquisition/change of parameters + sem_wait(&dataCallbackSemaphore[ithread]); + + //check to exit thread (for change of parameters) - only EXIT possibility + if(killAllDataCallbackThreads){ + cprintf(BLUE,"DataCallback_Thread %d:Goodbye!\n",ithread); + //free resources at exit + zmq_unbind(socket, hostName); + zmq_close(socket); + zmq_ctx_destroy(context); + pthread_exit(NULL); + } + + }/*--end of loop for each acquisition (outer loop) */ + +} + + void UDPStandardImplementation::startListening(){ @@ -2222,6 +2361,8 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ //ensure listening threads done before updating status as it returns to client (from stopReceiver) while(listeningThreadsMask) usleep(5000); + while(dataCallbackThreadsMask) + usleep(5000); //update status pthread_mutex_lock(&statusMutex); status = RUN_FINISHED; @@ -2485,22 +2626,19 @@ void UDPStandardImplementation::copyFrameToGui(int ithread, char* buffer, uint32 FILE_LOG(logDEBUG) << __AT__ << " called"; - //random read (gui not ready) - //need to toggle guiDataReady or the second frame wont be copied - if((!FrameToGuiFrequency) && (!guiData[ithread])){ -#ifdef DEBUG4 - cprintf(GREEN,"Writing_Thread: CopyingFrame: Resetting guiDataReady\n"); -#endif - pthread_mutex_lock(&dataReadyMutex); - guiDataReady[ithread]=0; - pthread_mutex_unlock(&dataReadyMutex); - } //if nthe frame, wait for your turn (1st frame always shown as its zero) - else if(FrameToGuiFrequency && ((frametoGuiCounter[ithread])%FrameToGuiFrequency)); + if(FrameToGuiFrequency && ((frametoGuiCounter[ithread])%FrameToGuiFrequency)); //random read (gui ready) or nth frame read: gui needs data now or it is the first frame else{ + + //tell datacallback to pick up data + sem_post(&dataCallbackSemaphore[ithread]); + + + memcpy(latestData[ithread],buffer , numpackets*onePacketSize); + #ifdef DEBUG4 cprintf(GREEN,"Writing_Thread: CopyingFrame: Gui needs data now OR 1st frame\n"); #endif From 3ed738b9491d6864fe6c095ffcde76e0389cb1a5 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Fri, 9 Sep 2016 17:52:07 +0200 Subject: [PATCH 17/48] almost done --- slsReceiverSoftware/Makefile | 15 +- .../include/UDPStandardImplementation.h | 32 +- slsReceiverSoftware/include/genericSocket.h | 1 + slsReceiverSoftware/include/libzmq.a | Bin 0 -> 2389564 bytes slsReceiverSoftware/include/zmq.h | 416 ++++++++++++++++++ .../src/UDPStandardImplementation.cpp | 402 +++++++++-------- .../src/slsReceiverTCPIPInterface.cpp | 79 +++- 7 files changed, 743 insertions(+), 202 deletions(-) create mode 100644 slsReceiverSoftware/include/libzmq.a create mode 100644 slsReceiverSoftware/include/zmq.h diff --git a/slsReceiverSoftware/Makefile b/slsReceiverSoftware/Makefile index b7d1c10a9..d94ac77a0 100644 --- a/slsReceiverSoftware/Makefile +++ b/slsReceiverSoftware/Makefile @@ -12,10 +12,13 @@ PROGS = $(DESTDIR)/slsReceiver CFLAGS= -g -DC_ONLY -fPIC #FLAGS+= #-DVERBOSE -DVERYVERBOSE -DFLAGS= -g -DDACS_INT -DSLS_RECEIVER_UDP_FUNCTIONS +DFLAGS= -g -DDACS_INT -DSLS_RECEIVER_UDP_FUNCTIONS #-DVERBOSE INCLUDES?= $(INCLUDESRXR) -I include/ -I ../slsDetectorCalibration +LIBZMQDIR = include +LIBZMQ = -L$(LIBZMQDIR) -Wl,-rpath=$(LIBZMQDIR) -lzmq + #-Iinclude -I../slsDetectorCalibration -I$(ASM) SRC_CLNT = MySocketTCP.cpp UDPInterface.cpp UDPBaseImplementation.cpp UDPStandardImplementation.cpp slsReceiverTCPIPInterface.cpp slsReceiver.cpp slsReceiverUsers.cpp utilities.cpp @@ -49,9 +52,9 @@ intdoc: $(SRC_H) $(SRC_CLNT) $(BUILDDIR)/%.o : $(SRCDIR)/%.cpp Makefile ifeq ($(ROOTSLS),yes) - $(CXX) -DROOTSLS -o $@ -c $< $(INCLUDES) $(DFLAGS) $(ROOTFLAGS) -fPIC $(EPICSFLAGS) $(LDFLAGRXR) -L/usr/lib64/ $(FLAGS) + $(CXX) -DROOTSLS -o $@ -c $< $(INCLUDES) $(DFLAGS) $(ROOTFLAGS) -fPIC $(EPICSFLAGS) $(LDFLAGRXR) -L/usr/lib64/ $(FLAGS) else - $(CXX) -o $@ -c $< $(INCLUDES) $(DFLAGS) -fPIC $(EPICSFLAGS) $(LDFLAGRXR) -lpthread $(FLAGS) + $(CXX) -o $@ -c $< $(INCLUDES) $(DFLAGS) -fPIC $(EPICSFLAGS) $(LDFLAGRXR) -lpthread $(FLAGS) $(LIBZMQ) -lrt endif lib: $(OBJS) $(DESTDIR)/libSlsReceiver.so $(DESTDIR)/libSlsReceiver.a @@ -60,7 +63,7 @@ receiver: $(DESTDIR)/slsReceiver $(DESTDIR)/libSlsReceiver.so: $(OBJS) - $(CXX) -shared -Wl,-soname,libSlsReceiver.so -o libSlsReceiver.so $(OBJS) -lc $(INCLUDES) $(DFLAGS) $(FLAGS) $(EPICSFLAGS) -L/usr/lib64 -lpthread + $(CXX) -shared -Wl,-soname,libSlsReceiver.so -o libSlsReceiver.so $(OBJS) -lc $(INCLUDES) $(DFLAGS) $(FLAGS) $(EPICSFLAGS) -L/usr/lib64 -lpthread $(LIBZMQ) -lrt $(shell test -d $(DESTDIR) || mkdir -p $(DESTDIR)) mv libSlsReceiver.so $(DESTDIR) @@ -70,12 +73,12 @@ $(DESTDIR)/libSlsReceiver.a: $(OBJS) $(DESTDIR)/slsReceiver: lib - $(CXX) -o $@ $(SRCDIR)/$(MAIN_SRC) $(FLAGS) $(INCLUDES) $(CLAGS) $(LIBS) $(LDFLAGRXR) -fPIC + $(CXX) -o $@ $(SRCDIR)/$(MAIN_SRC) $(FLAGS) $(INCLUDES) $(CLAGS) $(LIBS) $(LDFLAGRXR) -fPIC $(LIBZMQ) -lrt #$(EIGERFLAGS) $(DESTDIR)/dummyReceiver: lib - $(CXX) -o $@ $(SRCDIR)/$(DUMMY_MAIN_SRC) $(FLAGS) $(INCLUDES) $(CLAGS) $(LIBS) $(LDFLAGRXR) -fPIC + $(CXX) -o $@ $(SRCDIR)/$(DUMMY_MAIN_SRC) $(FLAGS) $(INCLUDES) $(CLAGS) $(LIBS) $(LDFLAGRXR) -fPIC $(LIBZMQ) -lrt #$(EIGERFLAGS) diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index 6674c62d9..15efda79f 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -70,6 +70,13 @@ class UDPStandardImplementation: private virtual slsReceiverDefs, public UDPBase void configure(map config_map); //*** file parameters*** + /** + * Set File Name Prefix (without frame index, file index and extension (_d0_f000000000000_8.raw)) + * Does not check for file existence since it is created only at startReceiver + * @param c file name (max of 1000 characters) + */ + void setFileName(const char c[]); + /** * Overridden method * Set data compression, by saving only hits (so far implemented only for Moench and Gotthard) @@ -500,10 +507,11 @@ private: * Get Frame Number * @param ithread writer thread index * @param wbuffer writer buffer - * @param tempframenumber reference to the frame number + * @param framenumber reference to the frame number + * @param packetnumber reference to the packet number * @return OK or FAIL */ - int getFrameNumber(int ithread, char* wbuffer, uint64_t &tempframenumber); + int getFrameandPacketNumber(int ithread, char* wbuffer, uint64_t &framenumber, uint32_t &packetnumber); /** * Find offset upto this frame number and write it to file @@ -530,6 +538,9 @@ private: #endif //**detector parameters*** + /*Detector Readout ID*/ + int detID; + /** Size of 1 buffer processed at a time */ int bufferSize; @@ -655,17 +666,17 @@ private: /** Current Frame copied for GUI */ char* latestData[MAX_NUMBER_OF_WRITER_THREADS]; - /** If Data to be sent to GUI is ready */ - bool guiDataReady[MAX_NUMBER_OF_WRITER_THREADS]; - - /** Pointer to data to be sent to GUI */ - char* guiData[MAX_NUMBER_OF_WRITER_THREADS]; - /** Pointer to file name to be sent to GUI */ char guiFileName[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; + /** Number of packets copied to be sent to gui (others padded) */ + int guiNumPackets[MAX_NUMBER_OF_WRITER_THREADS]; + /** Semaphore to synchronize Writer and GuiReader threads*/ - sem_t writerGuiSemaphore[MAX_NUMBER_OF_WRITER_THREADS]; + sem_t writerGuiSemaphore[MAX_NUMBER_OF_WRITER_THREADS]; //datacompression, only first thread sends to gui + + /** Semaphore to synchronize Writer and GuiReader threads*/ + sem_t dataCallbackWriterSemaphore[MAX_NUMBER_OF_WRITER_THREADS]; //datacompression, only first thread sends to gui /** counter for nth frame to gui */ int frametoGuiCounter[MAX_NUMBER_OF_WRITER_THREADS]; @@ -673,6 +684,9 @@ private: //***data call back thread parameters*** + /** Ensures if zmq threads created successfully */ + bool zmqThreadStarted; + /** Number of data callback Threads */ int numberofDataCallbackThreads; diff --git a/slsReceiverSoftware/include/genericSocket.h b/slsReceiverSoftware/include/genericSocket.h index 6cb9a8693..11828c8a7 100644 --- a/slsReceiverSoftware/include/genericSocket.h +++ b/slsReceiverSoftware/include/genericSocket.h @@ -81,6 +81,7 @@ using namespace std; #define DEFAULT_BACKLOG 5 #define DEFAULT_UDP_PORTNO 50001 #define DEFAULT_GUI_PORTNO 65000 +#define DEFAULT_ZMQ_PORTNO 70001 class genericSocket{ diff --git a/slsReceiverSoftware/include/libzmq.a b/slsReceiverSoftware/include/libzmq.a new file mode 100644 index 0000000000000000000000000000000000000000..0801ef205f2d87512924c08624ee43ee1885c2a4 GIT binary patch literal 2389564 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 my@Z13 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%0D23Bee|NhW{ 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. + + ************************************************************************* + NOTE to contributors. This file comprises the principal public contract + for ZeroMQ API users (along with zmq_utils.h). Any change to this file + supplied in a stable release SHOULD not break existing applications. + In practice this means that the value of constants must not change, and + that old values may not be reused for new constants. + ************************************************************************* +*/ + +#ifndef __ZMQ_H_INCLUDED__ +#define __ZMQ_H_INCLUDED__ + +/* Version macros for compile-time API version detection */ +#define ZMQ_VERSION_MAJOR 4 +#define ZMQ_VERSION_MINOR 0 +#define ZMQ_VERSION_PATCH 4 + +#define ZMQ_MAKE_VERSION(major, minor, patch) \ + ((major) * 10000 + (minor) * 100 + (patch)) +#define ZMQ_VERSION \ + ZMQ_MAKE_VERSION(ZMQ_VERSION_MAJOR, ZMQ_VERSION_MINOR, ZMQ_VERSION_PATCH) + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined _WIN32_WCE +#include +#endif +#include +#include +#if defined _WIN32 +#include +#endif + +/* Handle DSO symbol visibility */ +#if defined _WIN32 +# if defined ZMQ_STATIC +# define ZMQ_EXPORT +# elif defined DLL_EXPORT +# define ZMQ_EXPORT __declspec(dllexport) +# else +# define ZMQ_EXPORT __declspec(dllimport) +# endif +#else +# if defined __SUNPRO_C || defined __SUNPRO_CC +# define ZMQ_EXPORT __global +# elif (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER +# define ZMQ_EXPORT __attribute__ ((visibility("default"))) +# else +# define ZMQ_EXPORT +# endif +#endif + +/* Define integer types needed for event interface */ +#if defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_OPENVMS +# include +#elif defined _MSC_VER && _MSC_VER < 1600 +# ifndef int32_t +typedef __int32 int32_t; +# endif +# ifndef uint16_t +typedef unsigned __int16 uint16_t; +# endif +# ifndef uint8_t +typedef unsigned __int8 uint8_t; +# endif +#else +# include +#endif + + +/******************************************************************************/ +/* 0MQ errors. */ +/******************************************************************************/ + +/* A number random enough not to collide with different errno ranges on */ +/* different OSes. The assumption is that error_t is at least 32-bit type. */ +#define ZMQ_HAUSNUMERO 156384712 + +/* On Windows platform some of the standard POSIX errnos are not defined. */ +#ifndef ENOTSUP +#define ENOTSUP (ZMQ_HAUSNUMERO + 1) +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT (ZMQ_HAUSNUMERO + 2) +#endif +#ifndef ENOBUFS +#define ENOBUFS (ZMQ_HAUSNUMERO + 3) +#endif +#ifndef ENETDOWN +#define ENETDOWN (ZMQ_HAUSNUMERO + 4) +#endif +#ifndef EADDRINUSE +#define EADDRINUSE (ZMQ_HAUSNUMERO + 5) +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL (ZMQ_HAUSNUMERO + 6) +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED (ZMQ_HAUSNUMERO + 7) +#endif +#ifndef EINPROGRESS +#define EINPROGRESS (ZMQ_HAUSNUMERO + 8) +#endif +#ifndef ENOTSOCK +#define ENOTSOCK (ZMQ_HAUSNUMERO + 9) +#endif +#ifndef EMSGSIZE +#define EMSGSIZE (ZMQ_HAUSNUMERO + 10) +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT (ZMQ_HAUSNUMERO + 11) +#endif +#ifndef ENETUNREACH +#define ENETUNREACH (ZMQ_HAUSNUMERO + 12) +#endif +#ifndef ECONNABORTED +#define ECONNABORTED (ZMQ_HAUSNUMERO + 13) +#endif +#ifndef ECONNRESET +#define ECONNRESET (ZMQ_HAUSNUMERO + 14) +#endif +#ifndef ENOTCONN +#define ENOTCONN (ZMQ_HAUSNUMERO + 15) +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT (ZMQ_HAUSNUMERO + 16) +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH (ZMQ_HAUSNUMERO + 17) +#endif +#ifndef ENETRESET +#define ENETRESET (ZMQ_HAUSNUMERO + 18) +#endif + +/* Native 0MQ error codes. */ +#define EFSM (ZMQ_HAUSNUMERO + 51) +#define ENOCOMPATPROTO (ZMQ_HAUSNUMERO + 52) +#define ETERM (ZMQ_HAUSNUMERO + 53) +#define EMTHREAD (ZMQ_HAUSNUMERO + 54) + +/* Run-time API version detection */ +ZMQ_EXPORT void zmq_version (int *major, int *minor, int *patch); + +/* This function retrieves the errno as it is known to 0MQ library. The goal */ +/* of this function is to make the code 100% portable, including where 0MQ */ +/* compiled with certain CRT library (on Windows) is linked to an */ +/* application that uses different CRT library. */ +ZMQ_EXPORT int zmq_errno (void); + +/* Resolves system errors and 0MQ errors to human-readable string. */ +ZMQ_EXPORT const char *zmq_strerror (int errnum); + +/******************************************************************************/ +/* 0MQ infrastructure (a.k.a. context) initialisation & termination. */ +/******************************************************************************/ + +/* New API */ +/* Context options */ +#define ZMQ_IO_THREADS 1 +#define ZMQ_MAX_SOCKETS 2 + +/* Default for new contexts */ +#define ZMQ_IO_THREADS_DFLT 1 +#define ZMQ_MAX_SOCKETS_DFLT 1023 + +ZMQ_EXPORT void *zmq_ctx_new (void); +ZMQ_EXPORT int zmq_ctx_term (void *context); +ZMQ_EXPORT int zmq_ctx_shutdown (void *ctx_); +ZMQ_EXPORT int zmq_ctx_set (void *context, int option, int optval); +ZMQ_EXPORT int zmq_ctx_get (void *context, int option); + +/* Old (legacy) API */ +ZMQ_EXPORT void *zmq_init (int io_threads); +ZMQ_EXPORT int zmq_term (void *context); +ZMQ_EXPORT int zmq_ctx_destroy (void *context); + + +/******************************************************************************/ +/* 0MQ message definition. */ +/******************************************************************************/ + +typedef struct zmq_msg_t {unsigned char _ [32];} zmq_msg_t; + +typedef void (zmq_free_fn) (void *data, void *hint); + +ZMQ_EXPORT int zmq_msg_init (zmq_msg_t *msg); +ZMQ_EXPORT int zmq_msg_init_size (zmq_msg_t *msg, size_t size); +ZMQ_EXPORT int zmq_msg_init_data (zmq_msg_t *msg, void *data, + size_t size, zmq_free_fn *ffn, void *hint); +ZMQ_EXPORT int zmq_msg_send (zmq_msg_t *msg, void *s, int flags); +ZMQ_EXPORT int zmq_msg_recv (zmq_msg_t *msg, void *s, int flags); +ZMQ_EXPORT int zmq_msg_close (zmq_msg_t *msg); +ZMQ_EXPORT int zmq_msg_move (zmq_msg_t *dest, zmq_msg_t *src); +ZMQ_EXPORT int zmq_msg_copy (zmq_msg_t *dest, zmq_msg_t *src); +ZMQ_EXPORT void *zmq_msg_data (zmq_msg_t *msg); +ZMQ_EXPORT size_t zmq_msg_size (zmq_msg_t *msg); +ZMQ_EXPORT int zmq_msg_more (zmq_msg_t *msg); +ZMQ_EXPORT int zmq_msg_get (zmq_msg_t *msg, int option); +ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval); + + +/******************************************************************************/ +/* 0MQ socket definition. */ +/******************************************************************************/ + +/* Socket types. */ +#define ZMQ_PAIR 0 +#define ZMQ_PUB 1 +#define ZMQ_SUB 2 +#define ZMQ_REQ 3 +#define ZMQ_REP 4 +#define ZMQ_DEALER 5 +#define ZMQ_ROUTER 6 +#define ZMQ_PULL 7 +#define ZMQ_PUSH 8 +#define ZMQ_XPUB 9 +#define ZMQ_XSUB 10 +#define ZMQ_STREAM 11 + +/* Deprecated aliases */ +#define ZMQ_XREQ ZMQ_DEALER +#define ZMQ_XREP ZMQ_ROUTER + +/* Socket options. */ +#define ZMQ_AFFINITY 4 +#define ZMQ_IDENTITY 5 +#define ZMQ_SUBSCRIBE 6 +#define ZMQ_UNSUBSCRIBE 7 +#define ZMQ_RATE 8 +#define ZMQ_RECOVERY_IVL 9 +#define ZMQ_SNDBUF 11 +#define ZMQ_RCVBUF 12 +#define ZMQ_RCVMORE 13 +#define ZMQ_FD 14 +#define ZMQ_EVENTS 15 +#define ZMQ_TYPE 16 +#define ZMQ_LINGER 17 +#define ZMQ_RECONNECT_IVL 18 +#define ZMQ_BACKLOG 19 +#define ZMQ_RECONNECT_IVL_MAX 21 +#define ZMQ_MAXMSGSIZE 22 +#define ZMQ_SNDHWM 23 +#define ZMQ_RCVHWM 24 +#define ZMQ_MULTICAST_HOPS 25 +#define ZMQ_RCVTIMEO 27 +#define ZMQ_SNDTIMEO 28 +#define ZMQ_LAST_ENDPOINT 32 +#define ZMQ_ROUTER_MANDATORY 33 +#define ZMQ_TCP_KEEPALIVE 34 +#define ZMQ_TCP_KEEPALIVE_CNT 35 +#define ZMQ_TCP_KEEPALIVE_IDLE 36 +#define ZMQ_TCP_KEEPALIVE_INTVL 37 +#define ZMQ_TCP_ACCEPT_FILTER 38 +#define ZMQ_IMMEDIATE 39 +#define ZMQ_XPUB_VERBOSE 40 +#define ZMQ_ROUTER_RAW 41 +#define ZMQ_IPV6 42 +#define ZMQ_MECHANISM 43 +#define ZMQ_PLAIN_SERVER 44 +#define ZMQ_PLAIN_USERNAME 45 +#define ZMQ_PLAIN_PASSWORD 46 +#define ZMQ_CURVE_SERVER 47 +#define ZMQ_CURVE_PUBLICKEY 48 +#define ZMQ_CURVE_SECRETKEY 49 +#define ZMQ_CURVE_SERVERKEY 50 +#define ZMQ_PROBE_ROUTER 51 +#define ZMQ_REQ_CORRELATE 52 +#define ZMQ_REQ_RELAXED 53 +#define ZMQ_CONFLATE 54 +#define ZMQ_ZAP_DOMAIN 55 + +/* Message options */ +#define ZMQ_MORE 1 + +/* Send/recv options. */ +#define ZMQ_DONTWAIT 1 +#define ZMQ_SNDMORE 2 + +/* Security mechanisms */ +#define ZMQ_NULL 0 +#define ZMQ_PLAIN 1 +#define ZMQ_CURVE 2 + +/* Deprecated options and aliases */ +#define ZMQ_IPV4ONLY 31 +#define ZMQ_DELAY_ATTACH_ON_CONNECT ZMQ_IMMEDIATE +#define ZMQ_NOBLOCK ZMQ_DONTWAIT +#define ZMQ_FAIL_UNROUTABLE ZMQ_ROUTER_MANDATORY +#define ZMQ_ROUTER_BEHAVIOR ZMQ_ROUTER_MANDATORY + +/******************************************************************************/ +/* 0MQ socket events and monitoring */ +/******************************************************************************/ + +/* Socket transport events (tcp and ipc only) */ +#define ZMQ_EVENT_CONNECTED 1 +#define ZMQ_EVENT_CONNECT_DELAYED 2 +#define ZMQ_EVENT_CONNECT_RETRIED 4 + +#define ZMQ_EVENT_LISTENING 8 +#define ZMQ_EVENT_BIND_FAILED 16 + +#define ZMQ_EVENT_ACCEPTED 32 +#define ZMQ_EVENT_ACCEPT_FAILED 64 + +#define ZMQ_EVENT_CLOSED 128 +#define ZMQ_EVENT_CLOSE_FAILED 256 +#define ZMQ_EVENT_DISCONNECTED 512 +#define ZMQ_EVENT_MONITOR_STOPPED 1024 + +#define ZMQ_EVENT_ALL ( ZMQ_EVENT_CONNECTED | ZMQ_EVENT_CONNECT_DELAYED | \ + ZMQ_EVENT_CONNECT_RETRIED | ZMQ_EVENT_LISTENING | \ + ZMQ_EVENT_BIND_FAILED | ZMQ_EVENT_ACCEPTED | \ + ZMQ_EVENT_ACCEPT_FAILED | ZMQ_EVENT_CLOSED | \ + ZMQ_EVENT_CLOSE_FAILED | ZMQ_EVENT_DISCONNECTED | \ + ZMQ_EVENT_MONITOR_STOPPED) + +/* Socket event data */ +typedef struct { + uint16_t event; // id of the event as bitfield + int32_t value ; // value is either error code, fd or reconnect interval +} zmq_event_t; + +ZMQ_EXPORT void *zmq_socket (void *, int type); +ZMQ_EXPORT int zmq_close (void *s); +ZMQ_EXPORT int zmq_setsockopt (void *s, int option, const void *optval, + size_t optvallen); +ZMQ_EXPORT int zmq_getsockopt (void *s, int option, void *optval, + size_t *optvallen); +ZMQ_EXPORT int zmq_bind (void *s, const char *addr); +ZMQ_EXPORT int zmq_connect (void *s, const char *addr); +ZMQ_EXPORT int zmq_unbind (void *s, const char *addr); +ZMQ_EXPORT int zmq_disconnect (void *s, const char *addr); +ZMQ_EXPORT int zmq_send (void *s, const void *buf, size_t len, int flags); +ZMQ_EXPORT int zmq_send_const (void *s, const void *buf, size_t len, int flags); +ZMQ_EXPORT int zmq_recv (void *s, void *buf, size_t len, int flags); +ZMQ_EXPORT int zmq_socket_monitor (void *s, const char *addr, int events); + +ZMQ_EXPORT int zmq_sendmsg (void *s, zmq_msg_t *msg, int flags); +ZMQ_EXPORT int zmq_recvmsg (void *s, zmq_msg_t *msg, int flags); + +/* Experimental */ +struct iovec; + +ZMQ_EXPORT int zmq_sendiov (void *s, struct iovec *iov, size_t count, int flags); +ZMQ_EXPORT int zmq_recviov (void *s, struct iovec *iov, size_t *count, int flags); + +/******************************************************************************/ +/* I/O multiplexing. */ +/******************************************************************************/ + +#define ZMQ_POLLIN 1 +#define ZMQ_POLLOUT 2 +#define ZMQ_POLLERR 4 + +typedef struct +{ + void *socket; +#if defined _WIN32 + SOCKET fd; +#else + int fd; +#endif + short events; + short revents; +} zmq_pollitem_t; + +#define ZMQ_POLLITEMS_DFLT 16 + +ZMQ_EXPORT int zmq_poll (zmq_pollitem_t *items, int nitems, long timeout); + +/* Built-in message proxy (3-way) */ + +ZMQ_EXPORT int zmq_proxy (void *frontend, void *backend, void *capture); + +/* Encode a binary key as printable text using ZMQ RFC 32 */ +ZMQ_EXPORT char *zmq_z85_encode (char *dest, uint8_t *data, size_t size); + +/* Encode a binary key from printable text per ZMQ RFC 32 */ +ZMQ_EXPORT uint8_t *zmq_z85_decode (uint8_t *dest, char *string); + +/* Deprecated aliases */ +#define ZMQ_STREAMER 1 +#define ZMQ_FORWARDER 2 +#define ZMQ_QUEUE 3 +/* Deprecated method */ +ZMQ_EXPORT int zmq_device (int type, void *frontend, void *backend); + +#undef ZMQ_EXPORT + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 983fd1fb8..37e7c7406 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -17,6 +17,9 @@ #include #include #include + +#include //zmq + using namespace std; #define WRITE_HEADERS @@ -44,6 +47,7 @@ UDPStandardImplementation::UDPStandardImplementation(){ }else if(system("echo 250000 > /proc/sys/net/core/netdev_max_backlog")){ FILE_LOG(logDEBUG) << "Warning: No root permission to change max length of input queue in file /proc/sys/net/core/netdev_max_backlog"; } + /** permanent setting by heiner net.core.rmem_max = 104857600 # 100MiB net.core.netdev_max_backlog = 250000 @@ -85,12 +89,12 @@ void UDPStandardImplementation::deleteMembers(){ } for(int i=0; i config_map){ } +void UDPStandardImplementation::setFileName(const char c[]){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + char oldfilename[MAX_STR_LENGTH]; + strcpy(oldfilename,fileName); + + if(strlen(c)) + strcpy(fileName, c); + + if(strlen(fileName)){ + int detindex = -1; + string tempname(fileName); + size_t uscore=tempname.rfind("_"); + if (uscore!=string::npos){ + if (sscanf(tempname.substr(uscore+1,tempname.size()-uscore-1).c_str(),"d%d",&detindex)) { + detID = detindex; + } + } + if(detindex == -1) + detID = 0; + } + + + if(dataCallbackEnabled && (strcmp(oldfilename,fileName))){cout<<"***Going to destroy data callback threads and create!!!"<0) && (getFrameandPacketNumber(ithread, latestData[ithread]+offset, fnum, pnum)==FAIL)){ + offset+= onePacketSize; + } + + //end of buffer + if(offset >= size) + break; + + //new frame + if(currentfnum==-1){ + currentfnum = fnum; + } + + //last packet + if(pnum == packetsPerFrame){ + memcpy(buffer+((pnum-1)*oneDataSize), latestData[ithread]+offset+8,oneDataSize); + offset+= onePacketSize; + zmq_send(zmqsocket, buffer, oneframesize, 0); + memset(buffer,0xFF,oneframesize); + currentfnum = -1; + } + //same frame (not last) or next frame + else { + //next frame + if(fnum > currentfnum){ + zmq_send(zmqsocket, buffer, oneframesize, 0); + memset(buffer,0xFF,oneframesize); + currentfnum = fnum; + } + + memcpy(buffer+((pnum-1)*oneDataSize), latestData[ithread]+offset+8,oneDataSize); + offset+= onePacketSize; + } + + } - /*check if it should be added to previous (processlistening buffer for datacompression)*/ - zmq_send(socket, buffer.data(), buffer.size(), 0); }/*--end of loop for each buffer (inner loop)*/ + //free resources + delete[] buffer; + zmq_unbind(zmqsocket, hostName); + zmq_close(zmqsocket); + zmq_ctx_destroy(context); + + + //end of acquisition, wait for next acquisition/change of parameters sem_wait(&dataCallbackSemaphore[ithread]); + //check to exit thread (for change of parameters) - only EXIT possibility if(killAllDataCallbackThreads){ - cprintf(BLUE,"DataCallback_Thread %d:Goodbye!\n",ithread); - //free resources at exit - zmq_unbind(socket, hostName); - zmq_close(socket); - zmq_ctx_destroy(context); + cprintf(MAGENTA,"DataCallback_Thread %d:Goodbye!\n",ithread); pthread_exit(NULL); } @@ -1701,7 +1764,7 @@ void UDPStandardImplementation::startDataCallback(){ -void UDPStandardImplementation::startListening(){ +void UDPStandardImplementation::startListening(){cprintf(BLUE,"startlistening thread started %d\n",currentThreadIndex); FILE_LOG(logDEBUG) << __AT__ << " called"; //set current thread value index @@ -2136,7 +2199,7 @@ uint32_t UDPStandardImplementation::processListeningBuffer(int ithread, int &cSi -void UDPStandardImplementation::startWriting(){ +void UDPStandardImplementation::startWriting(){cprintf(GREEN,"start writing thread started %d\n",currentThreadIndex); FILE_LOG(logDEBUG) << __AT__ << " called"; //set current thread value index @@ -2157,7 +2220,6 @@ void UDPStandardImplementation::startWriting(){ //--reset parameters before acquisition nf = 0; - guiData[ithread] = latestData[ithread]; //so that the first frame is always copied if(dataCompressionEnable) listenfifoIndex = 0; //compression has only one listening thread @@ -2255,7 +2317,7 @@ void UDPStandardImplementation::waitWritingBufferForNextAcquisition(int ithread) if(myDetectorType == EIGER){ int detindex = -1; string tempname(fileName); - + //detid (more than 1 half module) size_t uscore=tempname.rfind("_"); if (uscore!=string::npos){ if (sscanf(tempname.substr(uscore+1,tempname.size()-uscore-1).c_str(),"d%d",&detindex)) { @@ -2263,11 +2325,12 @@ void UDPStandardImplementation::waitWritingBufferForNextAcquisition(int ithread) sprintf(fileNamePerThread[ithread],"%s_d%d",tempname.c_str(),detindex*2+ithread); } } - + //only one half module, so no detid if(detindex == -1) sprintf(fileNamePerThread[ithread],"%s_d%d",fileName,ithread); - } + }else + strcpy(fileNamePerThread[0],fileName); if(dataCompressionEnable){ #ifdef MYROOT1 @@ -2335,6 +2398,14 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ cprintf(YELLOW,"Writing_Thread %d: Freeing dummy-end buffer. Pushed into fifofree %p for listener %d\n", ithread,(void*)(wbuffer),ithread); #endif + if(dataCallbackEnabled){ + //ensure previous frame was processed + sem_wait(&writerGuiSemaphore[ithread]); + guiNumPackets[ithread] = dummyPacketValue; + //let it know its got data + sem_post(&dataCallbackWriterSemaphore[ithread]); + } + //all threads need to close file, reset mask and exit loop closeFile(ithread); @@ -2349,18 +2420,12 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ //thread 0 waits for all threads to finish & print statistics if(ithread == 0){ //wait for all other threads - if(dataCompressionEnable){ - cprintf(GREEN,"Writing_Thread %d: Waiting for jobs to be done.. current mask:0x%x\n",ithread, writerThreadsMask); - while(writerThreadsMask){ - /*cout << "." << flush;*/ - usleep(50000); - } - cprintf(GREEN,"Writing_Thread %d: Jobs Done!\n",ithread); - } - + while(writerThreadsMask) + usleep(5000); //ensure listening threads done before updating status as it returns to client (from stopReceiver) while(listeningThreadsMask) usleep(5000); + //ensure datacallbacks threads are done while(dataCallbackThreadsMask) usleep(5000); //update status @@ -2412,7 +2477,8 @@ void UDPStandardImplementation::handleWithoutDataCompression(int ithread, char* //get current frame number uint64_t tempframenumber; - if(getFrameNumber(ithread, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS,tempframenumber) == FAIL){ + uint32_t pnum; + if(getFrameandPacketNumber(ithread, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS,tempframenumber,pnum) == FAIL){ //error in frame number sent by fpga while(!fifoFree[ithread]->push(wbuffer)); @@ -2432,7 +2498,7 @@ void UDPStandardImplementation::handleWithoutDataCompression(int ithread, char* //callback to write data if (cbAction < DO_EVERYTHING) rawDataReadyCallBack((int)currentFrameNumber[ithread], wbuffer + HEADER_SIZE_NUM_TOT_PACKETS, npackets * onePacketSize, - sfilefd[ithread], guiData[ithread],pRawDataReady);//know which thread from sfilefd + sfilefd[ithread], latestData[ithread],pRawDataReady);//know which thread from sfilefd @@ -2446,7 +2512,7 @@ void UDPStandardImplementation::handleWithoutDataCompression(int ithread, char* //copy frame for gui //if(npackets >= (packetsPerFrame/numberofListeningThreads)) - if(npackets) + if(dataCallbackEnabled && npackets) copyFrameToGui(ithread, wbuffer,npackets); #ifdef DEBUG4 cprintf(GREEN,"Writing_Thread: Copied frame\n"); @@ -2487,8 +2553,9 @@ void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* w if(numpackets &&(lastFrameNumberInFile[ithread]>=0)){ //get start frame (required to create new file at the right juncture) uint64_t startframe =-1; + uint32_t pnum; //if(ithread) cout<<"getting start frame number"<push(wbuffer)); return; @@ -2545,7 +2612,8 @@ void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* w if(numpackets){ //get last frame number uint64_t finalLastFrameNumberToSave = 0; - if(getFrameNumber(ithread, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS + ((numpackets - 1) * onePacketSize), finalLastFrameNumberToSave) == FAIL){ + uint32_t pnum; + if(getFrameandPacketNumber(ithread, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS + ((numpackets - 1) * onePacketSize), finalLastFrameNumberToSave,pnum) == FAIL){ //error in frame number sent by fpga while(!fifoFree[ithread]->push(wbuffer)); return; @@ -2621,50 +2689,32 @@ void UDPStandardImplementation::updateFileHeader(int ithread){ } - +//called only if datacallback enabled void UDPStandardImplementation::copyFrameToGui(int ithread, char* buffer, uint32_t numpackets){ FILE_LOG(logDEBUG) << __AT__ << " called"; - - //if nthe frame, wait for your turn (1st frame always shown as its zero) if(FrameToGuiFrequency && ((frametoGuiCounter[ithread])%FrameToGuiFrequency)); //random read (gui ready) or nth frame read: gui needs data now or it is the first frame else{ - //tell datacallback to pick up data - sem_post(&dataCallbackSemaphore[ithread]); - - - memcpy(latestData[ithread],buffer , numpackets*onePacketSize); - #ifdef DEBUG4 - cprintf(GREEN,"Writing_Thread: CopyingFrame: Gui needs data now OR 1st frame\n"); + cprintf(GREEN,"Writing_Thread: CopyingFrame: Going to copy data\n"); #endif - pthread_mutex_lock(&dataReadyMutex); - guiDataReady[ithread]=0; -#ifdef DEBUG4 - cprintf(GREEN,"Writing_Thread: CopyingFrame: guidataready is 0, Copying data\n"); -#endif - memcpy(latestData[ithread],buffer , numpackets*onePacketSize); + //ensure previous frame was processed + sem_wait(&writerGuiSemaphore[ithread]); + + //copy date + guiNumPackets[ithread] = numpackets; strcpy(guiFileName[ithread],completeFileName[ithread]); - guiDataReady[ithread]=1; - pthread_mutex_unlock(&dataReadyMutex); -#ifdef DEBUG4 - cprintf(GREEN,"Writing_Thread: CopyingFrame: Copied Data, guidataready is 1\n"); -#endif + memcpy(latestData[ithread],buffer+ HEADER_SIZE_NUM_TOT_PACKETS , numpackets*onePacketSize); + //let it know its got data + sem_post(&dataCallbackWriterSemaphore[ithread]); - //nth frame read, block current process if the guireader hasnt read it yet - if(FrameToGuiFrequency){ #ifdef DEBUG4 - cprintf(GREEN,"Writing_Thread: CopyingFrame: Waiting after copying\n"); + cprintf(GREEN,"Writing_Thread: CopyingFrame: Copied Data\n"); #endif - sem_wait(&writerGuiSemaphore[ithread]); -#ifdef DEBUG4 - cprintf(GREEN,"Writing_Thread: CopyingFrame: Done waiting\n"); -#endif - } } @@ -2684,7 +2734,8 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer //get frame number uint64_t tempframenumber=-1; - if(getFrameNumber(ithread, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS, tempframenumber) == FAIL){ + uint32_t pnum; + if(getFrameandPacketNumber(ithread, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS, tempframenumber,pnum) == FAIL){ //error in frame number sent by fpga while(!fifoFree[ithread]->push(wbuffer)); return; @@ -2795,7 +2846,8 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer #endif if(!once){ - copyFrameToGui(ithread, buff[0],(uint32_t)packetsPerFrame); + if(dataCallbackEnabled) + copyFrameToGui(ithread, buff[0],(uint32_t)packetsPerFrame); once = 1; } } @@ -2819,59 +2871,60 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer -int UDPStandardImplementation::getFrameNumber(int ithread, char* wbuffer, uint64_t &tempframenumber){ +int UDPStandardImplementation::getFrameandPacketNumber(int ithread, char* wbuffer, uint64_t &framenumber, uint32_t &packetnumber){ FILE_LOG(logDEBUG) << __AT__ << " called"; eiger_packet_footer_t* footer=0; jfrau_packet_header_t* header=0; - int pnum=-1; switch(myDetectorType){ case EIGER: footer = (eiger_packet_footer_t*)(wbuffer + footerOffset); - tempframenumber = (uint32_t)(*( (uint64_t*) footer)); + framenumber = (uint32_t)(*( (uint64_t*) footer)); //error in frame number sent by fpga if(!((uint32_t)(*( (uint64_t*) footer)))){ - tempframenumber = -1; + framenumber = -1; FILE_LOG(logERROR) << "Fifo "<< ithread << ": Frame Number is zero from firmware."; return FAIL; } + packetnumber = (*( (uint16_t*) footer->packetNumber)); #ifdef DEBUG4 if(!ithread) cprintf(GREEN,"Writing_Thread %d: fnum:%lld pnum:%d FPGA_fnum:%d footeroffset:%d\n", ithread, - (long long int)tempframenumber, - (*( (uint16_t*) footer->packetNumber)), - (uint32_t)(*( (uint64_t*) footer)), + (long long int)framenumber, + packetnumber, + framenumber, footerOffset); #endif - tempframenumber += (startFrameIndex - 1); + framenumber += (startFrameIndex - 1); break; case JUNGFRAU: header = (jfrau_packet_header_t*)(wbuffer); - tempframenumber = (*( (uint32_t*) header->frameNumber))&frameIndexMask; + framenumber = (*( (uint32_t*) header->frameNumber))&frameIndexMask; + packetnumber = (uint32_t)(*( (uint8_t*) header->packetNumber)); #ifdef DEBUG4 cprintf(GREEN, "Writing_Thread %d: fnum:%lld\t pnum:%d\n", - (long long int)tempframenumber, - (*( (uint8_t*) header->packetNumber))); + (long long int)framenumber, + packetnumber); #endif - tempframenumber += startFrameIndex; + framenumber += startFrameIndex; break; default: - tempframenumber = ((uint32_t)(*((uint32_t*)(wbuffer)))); + framenumber = ((uint32_t)(*((uint32_t*)(wbuffer)))); //for gotthard and normal frame, increment frame number to separate fnum and pnum if (myDetectorType == PROPIX ||(myDetectorType == GOTTHARD && shortFrameEnable == -1)) - tempframenumber++; - pnum = tempframenumber&packetIndexMask; - tempframenumber = (tempframenumber & frameIndexMask) >> frameIndexOffset; + framenumber++; + packetnumber = framenumber&packetIndexMask; + framenumber = (framenumber & frameIndexMask) >> frameIndexOffset; #ifdef DEBUG4 cprintf(GREEN, "Writing_Thread %d: fnum:%lld\t pnum:%d\n", - (long long int)tempframenumber, - pnum); + (long long int)framenumber, + packetnumber); #endif - tempframenumber += startFrameIndex; + framenumber += startFrameIndex; break; } return OK; @@ -2890,9 +2943,10 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, int endoffset = startoffset + numpackets * onePacketSize; uint64_t tempframenumber=-1; offset = endoffset; + uint32_t pnum; //get last frame number - if(getFrameNumber(ithread, wbuffer + (endoffset-onePacketSize), tempframenumber) == FAIL){ + if(getFrameandPacketNumber(ithread, wbuffer + (endoffset-onePacketSize), tempframenumber,pnum) == FAIL){ //error in frame number sent by fpga while(!fifoFree[ithread]->push(wbuffer)); return FAIL; @@ -2918,7 +2972,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, offset -= bigIncrements; if(offsetpush(wbuffer)); return FAIL; @@ -2926,7 +2980,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } if(offsetpush(wbuffer)); return FAIL; @@ -2934,7 +2988,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } while(tempframenumberpush(wbuffer)); return FAIL; diff --git a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp index 500db2988..5d6f56973 100644 --- a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp +++ b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp @@ -353,6 +353,10 @@ int slsReceiverTCPIPInterface::set_detector_type(){ sprintf(mess,"Receiver locked by %s\n", socket->lastClientIP); ret=FAIL; } + else if((receiverBase)&&(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING)){ + strcpy(mess,"Can not set detector type while receiver not idle\n"); + ret = FAIL; + } else{ switch(dr){ @@ -443,6 +447,10 @@ int slsReceiverTCPIPInterface::set_file_name() { strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set file name while receiver not idle\n"); + ret = FAIL; + } else{ receiverBase->setFileName(fName); retval = receiverBase->getFileName(); @@ -506,15 +514,15 @@ int slsReceiverTCPIPInterface::set_file_dir() { if (lockStatus==1 && socket->differentClients==1){ sprintf(mess,"Receiver locked by %s\n", socket->lastClientIP); ret=FAIL; - }/* - else if((strlen(fPath))&&(receiverBase->getStatus()==RUNNING)){ - strcpy(mess,"Can not set file path while receiver running\n"); - ret = FAIL; - }*/ + } else if (receiverBase == NULL){ strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set file path while receiver not idle\n"); + ret = FAIL; + } else{ receiverBase->setFilePath(fPath); retval = receiverBase->getFilePath(); @@ -584,6 +592,10 @@ int slsReceiverTCPIPInterface::set_file_index() { strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set file index while receiver not idle\n"); + ret = FAIL; + } else{ if(index >= 0) receiverBase->setFileIndex(index); @@ -648,6 +660,10 @@ int slsReceiverTCPIPInterface::set_frame_index() { strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set frame index while receiver not idle\n"); + ret = FAIL; + } else{ //client sets to 0, but for receiver it is just an enable //client uses this value for other detectors not using receiver, @@ -725,9 +741,9 @@ int slsReceiverTCPIPInterface::setup_udp(){ strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } - else if(receiverBase->getStatus()==RUNNING){ + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set up udp while receiver not idle\n"); ret = FAIL; - strcpy(mess,"cannot set up udp when receiver is running\n"); } else{ //set up udp port @@ -859,9 +875,12 @@ int slsReceiverTCPIPInterface::stop_receiver(){ ret=FAIL; } else{ - if(receiverBase->getStatus()!=IDLE) + if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ receiverBase->stopReceiver(); + cout<<"receiver stopped"<getStatus(); + cout<<"to stop, receiver status:"<getStatus()==RUNNING){ + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ strcpy(mess,"Cannot set short frame while status is running\n"); ret=FAIL; } @@ -2077,6 +2096,10 @@ int slsReceiverTCPIPInterface::set_read_frequency(){ strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set receiver frequency mode while receiver not idle\n"); + ret = FAIL; + } /* else if((receiverBase->getStatus()==RUNNING) && (index >= 0)){ ret = FAIL; @@ -2142,6 +2165,10 @@ int slsReceiverTCPIPInterface::enable_file_write(){ strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set file write mode while receiver not idle\n"); + ret = FAIL; + } else{ if(enable >= 0) receiverBase->setFileWriteEnable(enable); @@ -2213,7 +2240,12 @@ int slsReceiverTCPIPInterface::start_readout(){ if (receiverBase == NULL){ strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; - }else{ + } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not start receiver readout while receiver not idle\n"); + ret = FAIL; + } + else{ receiverBase->startReadout(); retval = receiverBase->getStatus(); if((retval == TRANSMITTING) || (retval == RUN_FINISHED) || (retval == IDLE)) @@ -2269,6 +2301,10 @@ int slsReceiverTCPIPInterface::set_timer() { strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set timer while receiver not idle\n"); + ret = FAIL; + } else{ if(index[0] == FRAME_PERIOD){ if(index[1]>=0){ @@ -2344,7 +2380,7 @@ int slsReceiverTCPIPInterface::enable_compression() { strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } - else if(receiverBase->getStatus()==RUNNING){ + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ strcpy(mess,"Cannot enable/disable compression while status is running\n"); ret=FAIL; } @@ -2413,6 +2449,10 @@ int slsReceiverTCPIPInterface::set_detector_hostname() { strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set detector hostname while receiver not idle\n"); + ret = FAIL; + } else{ receiverBase->initialize(hostname); retval = receiverBase->getDetectorHostname(); @@ -2494,7 +2534,12 @@ int slsReceiverTCPIPInterface::set_dynamic_range() { if (receiverBase == NULL){ strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; - }else{ + } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set dynamic range while receiver not idle\n"); + ret = FAIL; + } + else{ if(dr > 0){ ret = receiverBase->setDynamicRange(dr); if(ret == FAIL) @@ -2571,6 +2616,10 @@ int slsReceiverTCPIPInterface::enable_overwrite() { strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set overwrite mode while receiver not idle\n"); + ret = FAIL; + } else{ if(index >= 0) receiverBase->setOverwriteEnable(index); @@ -2634,6 +2683,10 @@ int slsReceiverTCPIPInterface::enable_tengiga() { strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set up 1Giga/10Giga mode while receiver not idle\n"); + ret = FAIL; + } else{ if(val >= 0) ret = receiverBase->setTenGigaEnable(val); @@ -2699,7 +2752,7 @@ int slsReceiverTCPIPInterface::set_fifo_depth() { strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } - else if(receiverBase->getStatus()==RUNNING){ + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ strcpy(mess,"Cannot set/get fifo depth while status is running\n"); ret=FAIL; } From 7d86e6204541eef5c51cd18cea373bf1b1180de8 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Thu, 15 Sep 2016 12:16:08 +0200 Subject: [PATCH 18/48] almost --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 5 ++++- slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 37e7c7406..e43d253bd 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -450,10 +450,12 @@ void UDPStandardImplementation::setFileName(const char c[]){ createDataCallbackThreads(true); zmqThreadStarted = false; } + cout<<"***datacallback threads destroyed"<getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ receiverBase->stopReceiver(); - cout<<"receiver stopped"<getStatus(); - cout<<"to stop, receiver status:"< Date: Thu, 15 Sep 2016 17:16:00 +0200 Subject: [PATCH 19/48] works, need to do json header and send dataready --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index e43d253bd..7b31db4ae 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1664,7 +1664,7 @@ void UDPStandardImplementation::startDataCallback(){cprintf(MAGENTA,"start data void *context = zmq_ctx_new(); // create a publisher - void *zmqsocket = zmq_socket(context, ZMQ_PUB); + void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // bind zmq_bind(zmqsocket,hostName); @@ -1685,8 +1685,11 @@ void UDPStandardImplementation::startDataCallback(){cprintf(MAGENTA,"start data /**suing this in clientzmq_msg_more, * in serve use zmq_msg_send (&message, sender, ZMQ_SNDMORE); and 0 for last packet, but better to check lengt*/ + /*if (checkJoinThread()){for different scans + break; + }*/ zmq_send (zmqsocket, "end", 3, 0); - cprintf(BLUE,"sent done\n"); + //cprintf(BLUE,"sent done\n"); pthread_mutex_lock(&statusMutex); dataCallbackThreadsMask^=(1< Date: Fri, 16 Sep 2016 12:49:39 +0200 Subject: [PATCH 20/48] done --- .../include/UDPBaseImplementation.h | 22 ++++- slsReceiverSoftware/include/UDPInterface.h | 17 +++- .../include/UDPStandardImplementation.h | 13 ++- .../include/slsReceiverTCPIPInterface.h | 3 + .../include/sls_receiver_funcs.h | 4 +- .../src/UDPBaseImplementation.cpp | 25 ++++-- .../src/UDPStandardImplementation.cpp | 80 ++++++++++++------ .../src/slsReceiverTCPIPInterface.cpp | 82 +++++++++++++++++-- 8 files changed, 198 insertions(+), 48 deletions(-) diff --git a/slsReceiverSoftware/include/UDPBaseImplementation.h b/slsReceiverSoftware/include/UDPBaseImplementation.h index 60e07ef64..f576e2666 100644 --- a/slsReceiverSoftware/include/UDPBaseImplementation.h +++ b/slsReceiverSoftware/include/UDPBaseImplementation.h @@ -154,6 +154,13 @@ class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInter */ uint32_t getFrameToGuiFrequency() const; + /** + * Get the data stream enable + * @return 1 to send via zmq, else 0 + */ + uint32_t getDataStreamEnable() const; + + /** * Get Acquisition Period * @return acquisition period @@ -298,10 +305,17 @@ class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInter /** * Set the Frequency of Frames Sent to GUI - * @param i 0 for random frame requests, n for nth frame frequency + * @param freq 0 for random frame requests, n for nth frame frequency * @return OK or FAIL */ - int setFrameToGuiFrequency(const uint32_t i); + int setFrameToGuiFrequency(const uint32_t freq); + + /** + * Set the data stream enable + * @param enable 0 to disable, 1 to enable + * @return OK or FAIL + */ + uint32_t setDataStreamEnable(const uint32_t enable); /** * Set Acquisition Period @@ -525,7 +539,9 @@ class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInter /* Short Frame Enable or index of adc enabled, else -1 if all enabled (gotthard specific) TODO: move to setROI */ int shortFrameEnable; /** Frequency of Frames sent to GUI */ - uint32_t FrameToGuiFrequency; + uint32_t frameToGuiFrequency; + /** Data Stream Enable from Receiver */ + int32_t dataStreamEnable; diff --git a/slsReceiverSoftware/include/UDPInterface.h b/slsReceiverSoftware/include/UDPInterface.h index 754aa5bd7..9ef30c680 100644 --- a/slsReceiverSoftware/include/UDPInterface.h +++ b/slsReceiverSoftware/include/UDPInterface.h @@ -214,6 +214,12 @@ class UDPInterface { */ virtual uint32_t getFrameToGuiFrequency() const = 0; + /** + * Get the data stream enable + * @return 1 to send via zmq, else 0 + */ + virtual uint32_t getDataStreamEnable() const = 0; + /** * Get Acquisition Period * @return acquisition period @@ -355,10 +361,17 @@ class UDPInterface { /** * Set the Frequency of Frames Sent to GUI - * @param i 0 for random frame requests, n for nth frame frequency + * @param freq 0 for random frame requests, n for nth frame frequency * @return OK or FAIL */ - virtual int setFrameToGuiFrequency(const uint32_t i) = 0; + virtual int setFrameToGuiFrequency(const uint32_t freq) = 0; + + /** + * Set the data stream enable + * @param enable 0 to disable, 1 to enable + * @return OK or FAIL + */ + virtual uint32_t setDataStreamEnable(const uint32_t enable) = 0; /** * Set Acquisition Period diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index 15efda79f..18f9e546f 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -107,12 +107,18 @@ class UDPStandardImplementation: private virtual slsReceiverDefs, public UDPBase void setShortFrameEnable(const int i); /** - * Overridden method * Set the Frequency of Frames Sent to GUI - * @param i 0 for random frame requests, n for nth frame frequency + * @param freq 0 for random frame requests, n for nth frame frequency * @return OK or FAIL */ - int setFrameToGuiFrequency(const uint32_t i); + int setFrameToGuiFrequency(const uint32_t freq); + + /** + * Set the data stream enable + * @param enable 0 to disable, 1 to enable + * @return OK or FAIL + */ + uint32_t setDataStreamEnable(const uint32_t enable); /** * Overridden method @@ -702,7 +708,6 @@ private: /** Set to self-terminate data callback threads waiting for semaphores */ bool killAllDataCallbackThreads; - bool dataCallbackEnabled; //***general and listening thread parameters*** diff --git a/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h b/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h index 8b5c4527f..8b30be84f 100644 --- a/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h +++ b/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h @@ -176,6 +176,9 @@ private: /** Sets the receiver to send every nth frame to gui, or only upon gui request */ int set_read_frequency(); + /* Set the data stream enable */ + int set_data_stream_enable(); + /** Enable File Write*/ int enable_file_write(); diff --git a/slsReceiverSoftware/include/sls_receiver_funcs.h b/slsReceiverSoftware/include/sls_receiver_funcs.h index e1ef93040..cc3490c42 100644 --- a/slsReceiverSoftware/include/sls_receiver_funcs.h +++ b/slsReceiverSoftware/include/sls_receiver_funcs.h @@ -49,7 +49,9 @@ enum { F_ENABLE_RECEIVER_OVERWRITE, /**< set overwrite flag in receiver */ F_ENABLE_RECEIVER_TEN_GIGA, /**< enable 10Gbe in receiver */ - F_SET_RECEIVER_FIFO_DEPTH /**< set receiver fifo depth */ + F_SET_RECEIVER_FIFO_DEPTH, /**< set receiver fifo depth */ + + F_STREAM_DATA_FROM_RECEIVER /**< stream data from receiver to client */ /* Always append functions hereafter!!! */ }; diff --git a/slsReceiverSoftware/src/UDPBaseImplementation.cpp b/slsReceiverSoftware/src/UDPBaseImplementation.cpp index d4bbbcfbd..4032f27e8 100644 --- a/slsReceiverSoftware/src/UDPBaseImplementation.cpp +++ b/slsReceiverSoftware/src/UDPBaseImplementation.cpp @@ -75,7 +75,8 @@ void UDPBaseImplementation::initializeMembers(){ //***acquisition parameters*** shortFrameEnable = -1; - FrameToGuiFrequency = 0; + frameToGuiFrequency = 0; + dataStreamEnable = false; } UDPBaseImplementation::~UDPBaseImplementation(){} @@ -172,7 +173,9 @@ char *UDPBaseImplementation::getEthernetInterface() const{ /***acquisition parameters***/ int UDPBaseImplementation::getShortFrameEnable() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return shortFrameEnable;} -uint32_t UDPBaseImplementation::getFrameToGuiFrequency() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return FrameToGuiFrequency;} +uint32_t UDPBaseImplementation::getFrameToGuiFrequency() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return frameToGuiFrequency;} + +uint32_t UDPBaseImplementation::getDataStreamEnable() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return dataStreamEnable;} uint64_t UDPBaseImplementation::getAcquisitionPeriod() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return acquisitionPeriod;} @@ -314,16 +317,28 @@ void UDPBaseImplementation::setShortFrameEnable(const int i){ FILE_LOG(logINFO) << "Short Frame Enable: " << stringEnable(shortFrameEnable); } -int UDPBaseImplementation::setFrameToGuiFrequency(const uint32_t i){ +int UDPBaseImplementation::setFrameToGuiFrequency(const uint32_t freq){ FILE_LOG(logDEBUG) << __AT__ << " starting"; - FrameToGuiFrequency = i; - FILE_LOG(logINFO) << "Frame To Gui Frequency:" << FrameToGuiFrequency; + frameToGuiFrequency = freq; + FILE_LOG(logINFO) << "Frame To Gui Frequency:" << frameToGuiFrequency; //overrridden child classes might return FAIL return OK; } + +uint32_t UDPBaseImplementation::setDataStreamEnable(const uint32_t enable){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + dataStreamEnable = enable; + FILE_LOG(logINFO) << "Streaming Data from Receiver:" << dataStreamEnable; + + //overrridden child classes might return FAIL + return OK; +} + + int UDPBaseImplementation::setAcquisitionPeriod(const uint64_t i){ FILE_LOG(logDEBUG) << __AT__ << " starting"; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 7b31db4ae..22e9ef00c 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -195,7 +195,8 @@ void UDPStandardImplementation::initializeMembers(){ numberofDataCallbackThreads = 1; dataCallbackThreadsMask = 0x0; killAllDataCallbackThreads = false; - dataCallbackEnabled = true; /**false*/ + dataStreamEnable = false; + //***general and listening thread parameters*** threadStarted = false; @@ -282,8 +283,8 @@ int UDPStandardImplementation::setupFifoStructure(){ //else calculate best possible number of frames to listen to at a time (for fast readouts like gotthard) else{ //if frequency to gui is not random (every nth frame), then listen to only n frames per buffer - if(FrameToGuiFrequency) - numberofJobsPerBuffer = FrameToGuiFrequency; + if(frameToGuiFrequency) + numberofJobsPerBuffer = frameToGuiFrequency; //random frame sent to gui, then frames per buffer depends on acquisition period else{ //calculate 100ms/period to get frames to listen to at a time @@ -445,17 +446,13 @@ void UDPStandardImplementation::setFileName(const char c[]){ } - if(dataCallbackEnabled && (strcmp(oldfilename,fileName))){cout<<"***Going to destroy data callback threads and create!!!"<= (packetsPerFrame/numberofListeningThreads)) - if(dataCallbackEnabled && npackets) + if(dataStreamEnable && npackets) copyFrameToGui(ithread, wbuffer,npackets); #ifdef DEBUG4 cprintf(GREEN,"Writing_Thread: Copied frame\n"); @@ -2700,7 +2732,7 @@ void UDPStandardImplementation::copyFrameToGui(int ithread, char* buffer, uint32 FILE_LOG(logDEBUG) << __AT__ << " called"; //if nthe frame, wait for your turn (1st frame always shown as its zero) - if(FrameToGuiFrequency && ((frametoGuiCounter[ithread])%FrameToGuiFrequency)); + if(frameToGuiFrequency && ((frametoGuiCounter[ithread])%frameToGuiFrequency)); //random read (gui ready) or nth frame read: gui needs data now or it is the first frame else{ @@ -2725,7 +2757,7 @@ void UDPStandardImplementation::copyFrameToGui(int ithread, char* buffer, uint32 } //update the counter for nth frame - if(FrameToGuiFrequency) + if(frameToGuiFrequency) frametoGuiCounter[ithread]++; @@ -2852,7 +2884,7 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer #endif if(!once){ - if(dataCallbackEnabled) + if(dataStreamEnable) copyFrameToGui(ithread, buff[0],(uint32_t)packetsPerFrame); once = 1; } diff --git a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp index 1ee51c37d..ff39c681b 100644 --- a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp +++ b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp @@ -262,7 +262,7 @@ int slsReceiverTCPIPInterface::function_table(){ flist[F_ENABLE_RECEIVER_TEN_GIGA] = &slsReceiverTCPIPInterface::enable_tengiga; flist[F_SET_RECEIVER_FIFO_DEPTH] = &slsReceiverTCPIPInterface::set_fifo_depth; - + flist[F_STREAM_DATA_FROM_RECEIVER] = &slsReceiverTCPIPInterface::set_data_stream_enable; #ifdef VERYVERBOSE for (int i=0;igetStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ strcpy(mess,"Can not set receiver frequency mode while receiver not idle\n"); + cprintf(RED,"%s\n",mess); ret = FAIL; } - /* - else if((receiverBase->getStatus()==RUNNING) && (index >= 0)){ - ret = FAIL; - strcpy(mess,"cannot set up receiver mode when receiver is running\n"); - }*/ else{ - if(index >= 0){ + if(index >= 0 ){ ret = receiverBase->setFrameToGuiFrequency(index); - if(ret == FAIL) + if(ret == FAIL){ strcpy(mess, "Could not allocate memory for listening fifo\n"); + cprintf(RED,"%s\n",mess); + } } retval=receiverBase->getFrameToGuiFrequency(); - if(index>=0 && retval!=index) + if(index>=0 && retval!=index){ + strcpy(mess,"Could not set frame to gui frequency"); + cprintf(RED,"%s\n",mess); ret = FAIL; + } } } @@ -2138,6 +2139,69 @@ int slsReceiverTCPIPInterface::set_read_frequency(){ +int slsReceiverTCPIPInterface::set_data_stream_enable(){ + ret=OK; + int retval=-1; + int index; + strcpy(mess,"Could not set data stream enable\n"); + + + // receive arguments + if(socket->ReceiveDataOnly(&index,sizeof(index)) < 0 ){ + strcpy(mess,"Error reading from socket\n"); + ret = FAIL; + } + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + if (ret==OK) { + if (lockStatus==1 && socket->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", socket->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + strcpy(mess,"Can not set data stream enable while receiver not idle\n"); + cprintf(RED,"%s\n",mess); + ret = FAIL; + } + else{ + if(index >= 0 ) + ret = receiverBase->setDataStreamEnable(index); + retval=receiverBase->getDataStreamEnable(); + if(index>=0 && retval!=index){ + strcpy(mess,"Could not set data stream enable"); + cprintf(RED,"%s\n",mess); + ret = FAIL; + } + } + } + +#endif + + if(ret==OK && socket->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + socket->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + socket->SendDataOnly(mess,sizeof(mess)); + } + socket->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + int slsReceiverTCPIPInterface::enable_file_write(){ ret=OK; From 57e741c36cd33e78269fd8186e4817d1d563b5ed Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Fri, 16 Sep 2016 17:19:55 +0200 Subject: [PATCH 21/48] included json example with dummy values --- slsReceiverSoftware/CMakeLists.txt | 8 ++ .../src/UDPStandardImplementation.cpp | 82 ++++++++++++++++--- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/slsReceiverSoftware/CMakeLists.txt b/slsReceiverSoftware/CMakeLists.txt index 98f3574b2..79c9b6c73 100644 --- a/slsReceiverSoftware/CMakeLists.txt +++ b/slsReceiverSoftware/CMakeLists.txt @@ -18,6 +18,12 @@ include_directories( ../slsDetectorCalibration ) +add_library(zmq STATIC IMPORTED ) + +set_target_properties(zmq PROPERTIES + IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/include/libzmq.a +) + add_library(slsReceiverStatic STATIC ${SOURCES} ${HEADERS} @@ -45,4 +51,6 @@ set_target_properties(slsReceiver PROPERTIES target_link_libraries(slsReceiver slsReceiverShared pthread + zmq + rt ) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 22e9ef00c..b3b81318d 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -16,9 +16,10 @@ #include #include #include -#include +#include #include //zmq +#include using namespace std; @@ -1669,7 +1670,7 @@ void UDPStandardImplementation::startDataCallback(){cprintf(MAGENTA,"start data //set current thread value index int ithread = currentThreadIndex; - + struct timespec begin,end; // server address to bind char hostName[100] = "tcp://127.0.0.1:"; @@ -1686,22 +1687,24 @@ void UDPStandardImplementation::startDataCallback(){cprintf(MAGENTA,"start data memset(buffer,0xFF,oneframesize); int bufferoffset = 0; int size = 0; - int offset=0; - int currentfnum = 0; + int offset = 0; + int currentfnum = -1; uint64_t fnum = 0; uint32_t pnum = 0; - void *context = zmq_ctx_new(); - // create a publisher - void *zmqsocket = zmq_socket(context, ZMQ_PUSH); - // bind - zmq_bind(zmqsocket,hostName); + bool randomSendNow = true; + + void *context = zmq_ctx_new(); + void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // create a publisher + zmq_bind(zmqsocket,hostName); // bind //let calling function know thread started and obtained current (after sockets created) if(!zmqThreadStarted) zmqThreadStarted = true; - currentfnum = -1; + const char *type = "float64"; + const char *shape= "[1024, 512]"; + /* inner loop - loop for each buffer */ //until mask reset (dummy pcaket got by writer) while((1 << ithread) & dataCallbackThreadsMask){ @@ -1720,8 +1723,16 @@ void UDPStandardImplementation::startDataCallback(){cprintf(MAGENTA,"start data /*if (checkJoinThread()){for different scans break; }*/ + ostringstream header; + header << "{\"htype\":[\"chunk-1.0\"], " + << "\"type\":" << "\"" << type << "\", " + << "\"shape\":" << shape + << "}"; + //send header + zmq_send(zmqsocket, header.str().c_str(), header.str().length(), ZMQ_SNDMORE); + sleep(1); + //send data zmq_send (zmqsocket, "end", 3, 0); - //cprintf(BLUE,"sent done\n"); pthread_mutex_lock(&statusMutex); dataCallbackThreadsMask^=(1< currentfnum){ + ostringstream header; + header << "{\"htype\":[\"chunk-1.0\"], " + << "\"type\":" << "\"" << type << "\", " + << "\"shape\":" << shape + << "}"; + //send header + zmq_send(zmqsocket, header.str().c_str(), header.str().length(), ZMQ_SNDMORE); + sleep(1); + //send data zmq_send(zmqsocket, buffer, oneframesize, 0); +#ifdef DEBUG + cprintf(BLUE,"%d sent (last packet)\n",ithread); +#endif + //start clock after sending + if(!frameToGuiFrequency){ + randomSendNow = false; + clock_gettime(CLOCK_REALTIME, &begin); + } memset(buffer,0xFF,oneframesize); currentfnum = fnum; } From 82669103af1edd18f1605fbf04a95f624d633400 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Fri, 16 Sep 2016 17:22:44 +0200 Subject: [PATCH 22/48] added rapidjson lib --- .../include/rapidjson/allocators.h | 271 ++ .../include/rapidjson/document.h | 2575 +++++++++++++++++ .../include/rapidjson/encodedstream.h | 299 ++ .../include/rapidjson/encodings.h | 716 +++++ .../include/rapidjson/error/en.h | 74 + .../include/rapidjson/error/error.h | 155 + .../include/rapidjson/filereadstream.h | 99 + .../include/rapidjson/filewritestream.h | 104 + slsReceiverSoftware/include/rapidjson/fwd.h | 151 + .../include/rapidjson/internal/biginteger.h | 290 ++ .../include/rapidjson/internal/diyfp.h | 258 ++ .../include/rapidjson/internal/dtoa.h | 245 ++ .../include/rapidjson/internal/ieee754.h | 78 + .../include/rapidjson/internal/itoa.h | 304 ++ .../include/rapidjson/internal/meta.h | 181 ++ .../include/rapidjson/internal/pow10.h | 55 + .../include/rapidjson/internal/regex.h | 701 +++++ .../include/rapidjson/internal/stack.h | 230 ++ .../include/rapidjson/internal/strfunc.h | 55 + .../include/rapidjson/internal/strtod.h | 269 ++ .../include/rapidjson/internal/swap.h | 46 + .../include/rapidjson/istreamwrapper.h | 115 + .../include/rapidjson/memorybuffer.h | 70 + .../include/rapidjson/memorystream.h | 71 + .../include/rapidjson/msinttypes/inttypes.h | 316 ++ .../include/rapidjson/msinttypes/stdint.h | 300 ++ .../include/rapidjson/ostreamwrapper.h | 81 + .../include/rapidjson/pointer.h | 1358 +++++++++ .../include/rapidjson/prettywriter.h | 255 ++ .../include/rapidjson/rapidjson.h | 615 ++++ .../include/rapidjson/reader.h | 1879 ++++++++++++ .../include/rapidjson/schema.h | 2006 +++++++++++++ .../include/rapidjson/stream.h | 179 ++ .../include/rapidjson/stringbuffer.h | 117 + .../include/rapidjson/writer.h | 610 ++++ 35 files changed, 15128 insertions(+) create mode 100644 slsReceiverSoftware/include/rapidjson/allocators.h create mode 100644 slsReceiverSoftware/include/rapidjson/document.h create mode 100644 slsReceiverSoftware/include/rapidjson/encodedstream.h create mode 100644 slsReceiverSoftware/include/rapidjson/encodings.h create mode 100644 slsReceiverSoftware/include/rapidjson/error/en.h create mode 100644 slsReceiverSoftware/include/rapidjson/error/error.h create mode 100644 slsReceiverSoftware/include/rapidjson/filereadstream.h create mode 100644 slsReceiverSoftware/include/rapidjson/filewritestream.h create mode 100644 slsReceiverSoftware/include/rapidjson/fwd.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/biginteger.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/diyfp.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/dtoa.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/ieee754.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/itoa.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/meta.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/pow10.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/regex.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/stack.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/strfunc.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/strtod.h create mode 100644 slsReceiverSoftware/include/rapidjson/internal/swap.h create mode 100644 slsReceiverSoftware/include/rapidjson/istreamwrapper.h create mode 100644 slsReceiverSoftware/include/rapidjson/memorybuffer.h create mode 100644 slsReceiverSoftware/include/rapidjson/memorystream.h create mode 100644 slsReceiverSoftware/include/rapidjson/msinttypes/inttypes.h create mode 100644 slsReceiverSoftware/include/rapidjson/msinttypes/stdint.h create mode 100644 slsReceiverSoftware/include/rapidjson/ostreamwrapper.h create mode 100644 slsReceiverSoftware/include/rapidjson/pointer.h create mode 100644 slsReceiverSoftware/include/rapidjson/prettywriter.h create mode 100644 slsReceiverSoftware/include/rapidjson/rapidjson.h create mode 100644 slsReceiverSoftware/include/rapidjson/reader.h create mode 100644 slsReceiverSoftware/include/rapidjson/schema.h create mode 100644 slsReceiverSoftware/include/rapidjson/stream.h create mode 100644 slsReceiverSoftware/include/rapidjson/stringbuffer.h create mode 100644 slsReceiverSoftware/include/rapidjson/writer.h diff --git a/slsReceiverSoftware/include/rapidjson/allocators.h b/slsReceiverSoftware/include/rapidjson/allocators.h new file mode 100644 index 000000000..98affe03f --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/allocators.h @@ -0,0 +1,271 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return std::malloc(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + std::free(originalPtr); + return NULL; + } + return std::realloc(originalPtr, newSize); + } + static void Free(void *ptr) { std::free(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + RAPIDJSON_ASSERT(buffer != 0); + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while (chunkHead_ && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + if (chunkHead_ && chunkHead_ == userBuffer_) + chunkHead_->size = 0; // Clear user buffer + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + return true; + } + else + return false; + } + + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/slsReceiverSoftware/include/rapidjson/document.h b/slsReceiverSoftware/include/rapidjson/document.h new file mode 100644 index 000000000..e3e20dfbd --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/document.h @@ -0,0 +1,2575 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include + +RAPIDJSON_DIAG_PUSH +#ifdef _MSC_VER +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 6 +RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions +#endif +#endif // __GNUC__ + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::iterator, std::random_access_iterator_tag +#endif + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +struct GenericMember { + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator + : public std::iterator >::Type> { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef std::iterator BaseType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + //! Pointer to (const) GenericMember + typedef typename BaseType::pointer Pointer; + //! Reference to (const) GenericMember + typedef typename BaseType::reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef typename BaseType::difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } + bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } + bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } + bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } + bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } + bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +struct GenericMemberIterator; + +//! non-const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); } + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + GenericStringRef& operator=(const GenericStringRef& rhs) { s = rhs.s; length = rhs.length; } + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; +}; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str, internal::StrLen(str)); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template > +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[7] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_ASSERT(type <= kNumberType); + data_.f.flags = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \see CopyFrom() + */ + template< typename SourceAllocator > + GenericValue(const GenericValue& rhs, Allocator & allocator); + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_() { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(e); + } + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(GetStringPointer())); + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + RAPIDJSON_ASSERT(this != &rhs); + this->~GenericValue(); + RawAssign(rhs); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} + + //!@name Type + //@{ + + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast(std::numeric_limits::max())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast(std::numeric_limits::min())) + && (d < static_cast(std::numeric_limits::max())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-std::numeric_limits::max()) + || a > static_cast(std::numeric_limits::max())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + + // This will generate -Wexit-time-destructors in clang + // static GenericValue NullValue; + // return NullValue; + + // Use static buffer and placement-new to prevent destruction + static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + + ObjectData& o = data_.o; + if (o.size >= o.capacity) { + if (o.capacity == 0) { + o.capacity = kDefaultObjectCapacity; + SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); + } + else { + SizeType oldCapacity = o.capacity; + o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); + } + } + Member* members = GetMembersPointer(); + members[o.size].name.RawAssign(name); + members[o.size].value.RawAssign(value); + o.size++; + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) + *m = *last; // Move the last one to this place + else + m->~Member(); // Only one left, just destroy + --data_.o.size; + return m; + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); + std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + data_.o.size -= static_cast(last - first); + return pos; + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + GetElementsPointer()[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + GetElementsPointer()[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + return false; + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (const GenericValue* v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // Initial flags of different types. + kNullFlag = kNullType, + kTrueFlag = kTrueType | kBoolFlag, + kFalseFlag = kFalseType | kBoolFlag, + kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, + kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, + kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, + kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, + kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, + kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, + kConstStringFlag = kStringType | kStringFlag, + kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, + kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = 16; + static const SizeType kDefaultObjectCapacity = 16; + + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(e, values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); + std::memcpy(m, members, count * sizeof(Member)); + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(s); + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template , typename StackAllocator = CrtAllocator> +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(static_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + +// defined here due to the dependency on GenericDocument +template +template +inline +GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) +{ + switch (rhs.GetType()) { + case kObjectType: + case kArrayType: { // perform deep copy via SAX Handler + GenericDocument d(&allocator); + rhs.Accept(d); + RawAssign(*d.stack_.template Pop(1)); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } else { + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + } + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } +} + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + SizeType MemberCount() const { return value_.MemberCount(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { return value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/slsReceiverSoftware/include/rapidjson/encodedstream.h b/slsReceiverSoftware/include/rapidjson/encodedstream.h new file mode 100644 index 000000000..145068386 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/encodedstream.h @@ -0,0 +1,299 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" +#include "memorystream.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = reinterpret_cast(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam OutputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/slsReceiverSoftware/include/rapidjson/encodings.h b/slsReceiverSoftware/include/rapidjson/encodings.h new file mode 100644 index 000000000..baa7c2b17 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/encodings.h @@ -0,0 +1,716 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(overflow) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFF >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define COPY() os.Put(c = is.Take()) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c; + COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put((v & 0x3FF) | 0xDC00); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, (v & 0x3FF) | 0xDC00); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(is.Take()); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/slsReceiverSoftware/include/rapidjson/error/en.h b/slsReceiverSoftware/include/rapidjson/error/en.h new file mode 100644 index 000000000..2db838bff --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/error/en.h @@ -0,0 +1,74 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/slsReceiverSoftware/include/rapidjson/error/error.h b/slsReceiverSoftware/include/rapidjson/error/error.h new file mode 100644 index 000000000..95cb31a72 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/error/error.h @@ -0,0 +1,155 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { +public: + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). + operator bool() const { return !IsError(); } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/slsReceiverSoftware/include/rapidjson/filereadstream.h b/slsReceiverSoftware/include/rapidjson/filereadstream.h new file mode 100644 index 000000000..b56ea13b3 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/filereadstream.h @@ -0,0 +1,99 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/slsReceiverSoftware/include/rapidjson/filewritestream.h b/slsReceiverSoftware/include/rapidjson/filewritestream.h new file mode 100644 index 000000000..6378dd60e --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/filewritestream.h @@ -0,0 +1,104 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/slsReceiverSoftware/include/rapidjson/fwd.h b/slsReceiverSoftware/include/rapidjson/fwd.h new file mode 100644 index 000000000..e8104e841 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +struct GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/biginteger.h b/slsReceiverSoftware/include/rapidjson/internal/biginteger.h new file mode 100644 index 000000000..9d3e88c99 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/biginteger.h @@ -0,0 +1,290 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include // for _umul128 +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + BigInteger(const char* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Assume this != rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + RAPIDJSON_ASSERT(cmp != 0); + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + void AppendDecimal64(const char* begin, const char* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + static uint64_t ParseUint64(const char* begin, const char* end) { + uint64_t r = 0; + for (const char* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); + r = r * 10u + static_cast(*p - '0'); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/diyfp.h b/slsReceiverSoftware/include/rapidjson/internal/diyfp.h new file mode 100644 index 000000000..c9fefdc61 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/diyfp.h @@ -0,0 +1,258 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +struct DiyFp { + DiyFp() : f(), e() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#elif defined(__GNUC__) && __GNUC__ >= 4 + int s = __builtin_clzll(f); + return DiyFp(f << s, e - s); +#else + DiyFp res = *this; + while (!(res.f & (static_cast(1) << 63))) { + res.f <<= 1; + res.e--; + } + return res; +#endif + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + unsigned index = (static_cast(exp) + 348u) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); + } + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/dtoa.h b/slsReceiverSoftware/include/rapidjson/internal/dtoa.h new file mode 100644 index 000000000..8d6350e62 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/dtoa.h @@ -0,0 +1,245 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline unsigned CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default:; + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -static_cast(kappa); + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast(kappa)] : 0)); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (0 <= k && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + buffer[kk] = '.'; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); + Double d(value); + if (d.IsZero()) { + if (d.Sign()) + *buffer++ = '-'; // -0.0, Issue #289 + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K, maxDecimalPlaces); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/ieee754.h b/slsReceiverSoftware/include/rapidjson/internal/ieee754.h new file mode 100644 index 000000000..82bb0b99e --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/ieee754.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} + + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u_ + 1).Value(); + } + + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } + + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } + + static unsigned EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return static_cast(order) + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/itoa.h b/slsReceiverSoftware/include/rapidjson/internal/itoa.h new file mode 100644 index 000000000..01a4e7e72 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/itoa.h @@ -0,0 +1,304 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + if (value >= kTen8) + *buffer++ = cDigitsLut[d4 + 1]; + + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/meta.h b/slsReceiverSoftware/include/rapidjson/internal/meta.h new file mode 100644 index 000000000..5a9aaa428 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/meta.h @@ -0,0 +1,181 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif +#if defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/pow10.h b/slsReceiverSoftware/include/rapidjson/internal/pow10.h new file mode 100644 index 000000000..02f475d70 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/pow10.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/regex.h b/slsReceiverSoftware/include/rapidjson/internal/regex.h new file mode 100644 index 000000000..422a5240b --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/regex.h @@ -0,0 +1,701 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef typename Encoding::Ch Ch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream > ds(ss); + Parse(ds); + } + + ~GenericRegex() { + Allocator::Free(stateSet_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + + template + bool Match(InputStream& is) const { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) const { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) const { + return SearchWithAnchoring(is, anchorBegin_, anchorEnd_); + } + + bool Search(const Ch* s) const { + GenericStringStream is(s); + return Search(is); + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + template + class DecodedStream { + public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + + private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Allocator allocator; + Stack operandStack(&allocator, 256); // Frag + Stack operatorStack(&allocator, 256); // Operator + Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + + // Preallocate buffer for SearchWithAnchoring() + RAPIDJSON_ASSERT(stateSet_ == 0); + if (stateCount_ > 0) { + stateSet_ = static_cast(states_.GetAllocator().Malloc(GetStateSetSize())); + state0_.template Reserve(stateCount_); + state1_.template Reserve(stateCount_); + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + default: + RAPIDJSON_ASSERT(op == kOneOrMore); + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const { + RAPIDJSON_ASSERT(IsValid()); + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == kAnyCharacterClass || + (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) const { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) { + stateSet_[index >> 5] |= (1 << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = GetRange(rangeIndex); + if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + uint32_t* stateSet_; // allocated by states_.GetAllocator() + mutable Stack state0_; + mutable Stack state1_; + bool anchorBegin_; + bool anchorEnd_; +}; + +typedef GenericRegex > Regex; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/stack.h b/slsReceiverSoftware/include/rapidjson/internal/stack.h new file mode 100644 index 000000000..022c9aab4 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/stack.h @@ -0,0 +1,230 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) + Expand(count); + } + + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STACK_H_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/strfunc.h b/slsReceiverSoftware/include/rapidjson/internal/strfunc.h new file mode 100644 index 000000000..2edfae526 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/strfunc.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/strtod.h b/slsReceiverSoftware/include/rapidjson/internal/strtod.h new file mode 100644 index 000000000..289c413b0 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/strtod.h @@ -0,0 +1,269 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + + BigInteger delta(0); + dS.Difference(bS, &delta); + + return delta.Compare(hS); +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { + uint64_t significand = 0; + size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < length; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + break; + significand = significand * 10u + static_cast(decimals[i] - '0'); + } + + if (i < length && decimals[i] >= '5') // Rounding + significand++; + + size_t remaining = length - i; + const unsigned kUlpShift = 3; + const unsigned kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp - 1; + RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); + v = v * kPow10[adjustment]; + if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + unsigned precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + unsigned scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + static_cast(kUlp); + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + static_cast(precisionSize)); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + static_cast(error)) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { + const BigInteger dInt(decimals, length); + const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; + Double a(approx); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + return a.NextPositiveDouble(); +} + +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result; + if (StrtodFast(d, p, &result)) + return result; + + // Trim leading zeros + while (*decimals == '0' && length > 1) { + length--; + decimals++; + decimalPosition--; + } + + // Trim trailing zeros + while (decimals[length - 1] == '0' && length > 1) { + length--; + decimalPosition--; + exp++; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 780; + if (static_cast(length) > kMaxDecimalDigit) { + int delta = (static_cast(length) - kMaxDecimalDigit); + exp += delta; + decimalPosition -= static_cast(delta); + length = kMaxDecimalDigit; + } + + // If too small, underflow to zero + if (int(length) + exp < -324) + return 0.0; + + if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, length, decimalPosition, exp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/slsReceiverSoftware/include/rapidjson/internal/swap.h b/slsReceiverSoftware/include/rapidjson/internal/swap.h new file mode 100644 index 000000000..666e49f97 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/internal/swap.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_SWAP_H_ +#define RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ header +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. + \note This has the same semantics as std::swap(). +*/ +template +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/slsReceiverSoftware/include/rapidjson/istreamwrapper.h b/slsReceiverSoftware/include/rapidjson/istreamwrapper.h new file mode 100644 index 000000000..f5fe28977 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/istreamwrapper.h @@ -0,0 +1,115 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} + + Ch Peek() const { + typename StreamType::int_type c = stream_.peek(); + return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : '\0'; + } + + Ch Take() { + typename StreamType::int_type c = stream_.get(); + if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { + count_++; + return static_cast(c); + } + else + return '\0'; + } + + // tellg() may return -1 when failed. So we count by ourself. + size_t Tell() const { return count_; } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. + int i; + bool hasError = false; + for (i = 0; i < 4; ++i) { + typename StreamType::int_type c = stream_.get(); + if (c == StreamType::traits_type::eof()) { + hasError = true; + stream_.clear(); + break; + } + peekBuffer_[i] = static_cast(c); + } + for (--i; i >= 0; --i) + stream_.putback(peekBuffer_[i]); + return !hasError ? peekBuffer_ : 0; + } + +private: + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + StreamType& stream_; + size_t count_; //!< Number of characters read. Note: + mutable Ch peekBuffer_[4]; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/slsReceiverSoftware/include/rapidjson/memorybuffer.h b/slsReceiverSoftware/include/rapidjson/memorybuffer.h new file mode 100644 index 000000000..39bee1dec --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/memorybuffer.h @@ -0,0 +1,70 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/slsReceiverSoftware/include/rapidjson/memorystream.h b/slsReceiverSoftware/include/rapidjson/memorystream.h new file mode 100644 index 000000000..1d71d8a4f --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/memorystream.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/slsReceiverSoftware/include/rapidjson/msinttypes/inttypes.h b/slsReceiverSoftware/include/rapidjson/msinttypes/inttypes.h new file mode 100644 index 000000000..18111286b --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,316 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/slsReceiverSoftware/include/rapidjson/msinttypes/stdint.h b/slsReceiverSoftware/include/rapidjson/msinttypes/stdint.h new file mode 100644 index 000000000..3d4477b9a --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/msinttypes/stdint.h @@ -0,0 +1,300 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if defined(__cplusplus) && !defined(_M_ARM) +extern "C" { +#endif +# include +#if defined(__cplusplus) && !defined(_M_ARM) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/slsReceiverSoftware/include/rapidjson/ostreamwrapper.h b/slsReceiverSoftware/include/rapidjson/ostreamwrapper.h new file mode 100644 index 000000000..6f4667c08 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/slsReceiverSoftware/include/rapidjson/pointer.h b/slsReceiverSoftware/include/rapidjson/pointer.h new file mode 100644 index 000000000..0206ac1c8 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/pointer.h @@ -0,0 +1,1358 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POINTER_H_ +#define RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "internal/itoa.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, StrLen(name), allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + if (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = buffer[i]; + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + RAPIDJSON_ASSERT(token.IsUint64()); + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } + + // Adjust pointers to name buffer + std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; + for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) + t->name += diff; + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + RAPIDJSON_ASSERT(source != NULL); + RAPIDJSON_ASSERT(nameBuffer_ == 0); + RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(hexDigits[u >> 4]); + os_.Put(hexDigits[u & 15]); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/slsReceiverSoftware/include/rapidjson/prettywriter.h b/slsReceiverSoftware/include/rapidjson/prettywriter.h new file mode 100644 index 000000000..0dcb0fee9 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/prettywriter.h @@ -0,0 +1,255 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of ouptut os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + PrettyPrefix(kNumberType); + return Base::WriteString(str, length); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + PrettyPrefix(kStringType); + return Base::WriteString(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::WriteEndObject(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::WriteEndArray(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(type); return Base::WriteRawValue(json, length); } + +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); + } + + if (!(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, static_cast(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/slsReceiverSoftware/include/rapidjson/rapidjson.h b/slsReceiverSoftware/include/rapidjson/rapidjson.h new file mode 100644 index 000000000..053b2ce43 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/rapidjson.h @@ -0,0 +1,615 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overriden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x +//!@endcond + +/*! \def RAPIDJSON_MAJOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_MINOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_PATCH_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_VERSION_STRING + \ingroup RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define RAPIDJSON_MAJOR_VERSION 1 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 +#define RAPIDJSON_VERSION_STRING \ + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && defined(_M_ARM) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. Currently the default uses 4 bytes + alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. + User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#if RAPIDJSON_64BIT == 1 +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#else +#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2 optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible + processors. + + To enable these optimizations, two different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + \c RAPIDJSON_SSE42 takes precedence, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Adopt from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y + +#if defined(__GNUC__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(x) new x +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/slsReceiverSoftware/include/rapidjson/reader.h b/slsReceiverSoftware/include/rapidjson/reader.h new file mode 100644 index 000000000..19f8849b1 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/reader.h @@ -0,0 +1,1879 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_SSE2 + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n'); + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (RAPIDJSON_UNLIKELY(!success)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const char* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + RAPIDJSON_FORCEINLINE void Push(char c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + useNanOrInf = true; + if (RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) { + d = std::numeric_limits::quiet_NaN(); + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + if (use64bit) { + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (Consume(s, '.')) { + decimalPosition = s.Length(); + + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (Consume(s, 'e') || Consume(s, 'E')) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (exp >= 214748364) { // Issue #313: prevent overflow exponent + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + StringStream srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingStartState = 0, + IterativeParsingFinishState, + IterativeParsingErrorState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingKeyValueDelimiterState, + IterativeParsingMemberValueState, + IterativeParsingMemberDelimiterState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingElementDelimiterState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState + }; + + enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + } + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/slsReceiverSoftware/include/rapidjson/schema.h b/slsReceiverSoftware/include/rapidjson/schema.h new file mode 100644 index 000000000..b182aa27f --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/schema.h @@ -0,0 +1,2006 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +#if RAPIDJSON_SCHEMA_VERBOSE +#include "stringbuffer.h" +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeyword(const char* keyword) { + printf("Fail keyword: %s\n", keyword); +} + +inline void PrintInvalidKeyword(const wchar_t* keyword) { + wprintf(L"Fail keyword: %ls\n", keyword); +} + +inline void PrintInvalidDocument(const char* document) { + printf("Fail document: %s\n\n", document); +} + +inline void PrintInvalidDocument(const wchar_t* document) { + wprintf(L"Fail document: %ls\n\n", document); +} + +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) +#else +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) +#endif + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidKeyword = keyword.GetString();\ + RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : + factory(f), + schema(s), + valueSchema(), + invalidKeyword(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) + factory.DestroySchemaValidator(validators[i]); + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + const SchemaType* schema; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + allocator_(allocator), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false) + { + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + if (!value.IsObject()) + return; + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256 + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + + if (schemaDocument) { + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + } + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = GetTypeless(); + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + } + } + + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + } + + ~Schema() { + if (allocator_) { + allocator_->Free(enum_); + } + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + allocator_->Free(pattern_); + } +#endif + } + + bool BeginValue(Context& context) const { + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = GetTypeless(); + else + RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + } + else + context.valueSchema = GetTypeless(); + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + + if (enum_) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); + foundEnum:; + } + + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + else + oneValid = true; + } + if (!oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + + return true; + } + + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + if (!(type_ & (1 << kNumberSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + if (!(type_ & (1 << kStringSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); + if (count > maxLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + } + + SizeType index; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = GetTypeless(); + return true; + } + + if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + if (hasRequired_) + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required) + if (!context.propertyExist[index]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + + if (memberCount < minProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + + if (memberCount > maxProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + + if (hasDependencies_) { + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) + if (context.propertyExist[sourceIndex]) { + if (properties_[sourceIndex].dependencies) { + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + else if (properties_[sourceIndex].dependenciesSchema) + if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + } + + return true; + } + + bool StartArray(Context& context) const { + if (!(type_ & (1 << kArraySchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + context.arrayElementIndex = 0; + context.inArray = true; + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + context.inArray = false; + + if (elementCount < minItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); + + if (elementCount > maxItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); + + return true; + } + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + static const SchemaType* GetTypeless() { + static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); + return &typeless; + } + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); + if (!r->IsValid()) { + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + return pattern->Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) + try { + return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { return 0; } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + context.validatorCount = validatorCount_; + + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); + } + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsUint64()) { + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsUint64()) + /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + return true; + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = buffer[i]; + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + */ + explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize) + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call AddRefSchema() if there are $ref. + CreateSchemaRecursive(&root_, PointerType(), document, document); + + // Resolve $ref + while (!schemaRef_.Empty()) { + SchemaRefEntry* refEntry = schemaRef_.template Pop(1); + if (const SchemaType* s = GetSchema(refEntry->target)) { + if (refEntry->schema) + *refEntry->schema = s; + + // Create entry in map if not exist + if (!GetSchema(refEntry->source)) { + new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); + } + } + refEntry->~SchemaRefEntry(); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + +private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + struct SchemaRefEntry { + SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} + PointerType source; + PointerType target; + const SchemaType** schema; + }; + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + if (schema) + *schema = SchemaType::GetTypeless(); + + if (v.GetType() == kObjectType) { + const SchemaType* s = GetSchema(pointer); + if (!s) + CreateSchema(schema, pointer, v, document); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + } + + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + RAPIDJSON_ASSERT(pointer.IsValid()); + if (v.IsObject()) { + if (!HandleRefSchema(pointer, schema, v, document)) { + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); + new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (schema) + *schema = s; + } + } + } + + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; + static const ValueType kRefValue(kRefString, 4); + + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + if (itr == v.MemberEnd()) + return false; + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len > 0) { + const Ch* s = itr->value.GetString(); + SizeType i = 0; + while (i < len && s[i] != '#') // Find the first # + i++; + + if (i > 0) { // Remote reference, resolve immediately + if (remoteProvider_) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) { + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + return true; + } + } + } + } + } + else if (s[i] == '#') { // Local reference, defer resolution + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const ValueType* nv = pointer.Get(document)) + if (HandleRefSchema(source, schema, *nv, document)) + return true; + + new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); + return true; + } + } + } + } + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator +{ +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + outputHandler_(GetNullHandler()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + outputHandler_(outputHandler), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + valid_ = true; + } + + //! Checks whether the current state is valid. + // Implementation of ISchemaValidator + virtual bool IsValid() const { return valid_; } + + //! Gets the JSON pointer pointed to the invalid schema. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + } + + //! Gets the keyword of invalid schema. + const Ch* GetInvalidSchemaKeyword() const { + return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; + } + + //! Gets the JSON pointer pointed to the invalid value. + PointerType GetInvalidDocumentPointer() const { + return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + internal::PrintInvalidDocument(documentStack_.template Bottom());\ +RAPIDJSON_MULTILINEMACRO_END +#else +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() +#endif + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if (!BeginValue() || !CurrentSchema().method arg1) {\ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ + return valid_ = false;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + return valid_ = EndValue() && outputHandler_.method arg2 + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + return valid_ = outputHandler_.StartObject(); + } + + bool Key(const Ch* str, SizeType len, bool copy) { + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + return valid_ = outputHandler_.Key(str, len, copy); + } + + bool EndObject(SizeType memberCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + return valid_ = outputHandler_.StartArray(); + } + + bool EndArray(SizeType elementCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { + return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, +#if RAPIDJSON_SCHEMA_VERBOSE + depth_ + 1, +#endif + &GetStateAllocator()); + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + return StateAllocator::Free(p); + } + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth, +#endif + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + outputHandler_(GetNullHandler()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(depth) +#endif + { + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator()); + return *stateAllocator_; + } + + bool BeginValue() { + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext())) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + if (CurrentContext().valueSchema) + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i]); + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + if (!CurrentSchema().EndValue(CurrentContext())) + return false; + +#if RAPIDJSON_SCHEMA_VERBOSE + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); + + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); +#endif + + uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + if (context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) + RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static OutputHandler& GetNullHandler() { + static OutputHandler nullHandler; + return nullHandler; + } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + OutputHandler& outputHandler_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + bool valid_; +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth_; +#endif +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/slsReceiverSoftware/include/rapidjson/stream.h b/slsReceiverSoftware/include/rapidjson/stream.h new file mode 100644 index 000000000..fef82c252 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/stream.h @@ -0,0 +1,179 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/slsReceiverSoftware/include/rapidjson/stringbuffer.h b/slsReceiverSoftware/include/rapidjson/stringbuffer.h new file mode 100644 index 000000000..78f34d209 --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/stringbuffer.h @@ -0,0 +1,117 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/slsReceiverSoftware/include/rapidjson/writer.h b/slsReceiverSoftware/include/rapidjson/writer.h new file mode 100644 index 000000000..94f22dd5f --- /dev/null +++ b/slsReceiverSoftware/include/rapidjson/writer.h @@ -0,0 +1,610 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kStringType); + return EndValue(WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { Prefix(type); return EndValue(WriteRawValue(json, length)); } + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + static const size_t kDefaultLevelDepth = 32; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename TargetEncoding::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (ScanWriteUnescapedString(is, length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + for (size_t i = 0; i < length; i++) { + RAPIDJSON_ASSERT(json[i] != '\0'); + PutUnsafe(*os_, json[i]); + } + return true; + } + + void Prefix(Type type) { + (void)type; + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + os_->Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + +RAPIDJSON_NAMESPACE_END + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ From 6a244c10573ed47ca66a0d21d5ddf736352ee045 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Mon, 19 Sep 2016 12:34:38 +0200 Subject: [PATCH 23/48] bug fixed, rapidjson parse error --- .../src/UDPStandardImplementation.cpp | 95 ++++++++++--------- 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index b3b81318d..0158d2107 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1053,6 +1053,7 @@ void UDPStandardImplementation::stopReceiver(){ } //semaphore destroy + cout<<"gonna destroy writerguisemphore"< currentfnum){ ostringstream header; - header << "{\"htype\":[\"chunk-1.0\"], " - << "\"type\":" << "\"" << type << "\", " - << "\"shape\":" << shape - << "}"; - //send header - zmq_send(zmqsocket, header.str().c_str(), header.str().length(), ZMQ_SNDMORE); - sleep(1); - //send data + header << "{\"htype\":[\"chunk-1.0\"], " + << "\"type\":" << "\"" << type << "\", " + << "\"shape\":" << shape + << "}"; + //cout< Date: Mon, 19 Sep 2016 17:21:28 +0200 Subject: [PATCH 24/48] only missing data left to be handled in zmqthread in receiver --- .../include/UDPStandardImplementation.h | 3 +- .../src/UDPStandardImplementation.cpp | 122 +++++++++--------- 2 files changed, 61 insertions(+), 64 deletions(-) diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index 18f9e546f..14e0f638f 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -515,9 +515,10 @@ private: * @param wbuffer writer buffer * @param framenumber reference to the frame number * @param packetnumber reference to the packet number + * @param subframenumber reference to the subframe number * @return OK or FAIL */ - int getFrameandPacketNumber(int ithread, char* wbuffer, uint64_t &framenumber, uint32_t &packetnumber); + int getFrameandPacketNumber(int ithread, char* wbuffer, uint64_t &framenumber, uint32_t &packetnumber, uint32_t &subframenumber); /** * Find offset upto this frame number and write it to file diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 0158d2107..68f850c53 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -568,23 +568,18 @@ uint32_t UDPStandardImplementation::setDataStreamEnable(const uint32_t enable){ FILE_LOG(logDEBUG) << __AT__ << " called"; - cout<<"************datasend:"<0) && (getFrameandPacketNumber(ithread, latestData[ithread]+offset, fnum, pnum)==FAIL)){ + while((size>0) && (getFrameandPacketNumber(ithread, latestData[ithread]+offset, fnum, pnum,snum)==FAIL)){ offset+= onePacketSize; } @@ -1780,24 +1774,23 @@ void UDPStandardImplementation::startDataCallback(){ //new frame if(currentfnum==-1){ currentfnum = fnum; + //update frame details + frameIndex = fnum; + acquisitionIndex = fnum - startAcquisitionIndex; + if(dynamicRange == 32) subframeIndex = snum; } //last packet if(pnum == packetsPerFrame){ memcpy(buffer+((pnum-1)*oneDataSize), latestData[ithread]+offset+8,oneDataSize); offset+= onePacketSize; - ostringstream header; - header << "{\"htype\":[\"chunk-1.0\"], " - << "\"type\":" << "\"" << type << "\", " - << "\"shape\":" << shape - << "}"; - //cout< currentfnum){ - ostringstream header; - header << "{\"htype\":[\"chunk-1.0\"], " - << "\"type\":" << "\"" << type << "\", " - << "\"shape\":" << shape - << "}"; - //cout<push(wbuffer)); @@ -2660,8 +2647,9 @@ void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* w //get start frame (required to create new file at the right juncture) uint64_t startframe =-1; uint32_t pnum; + uint32_t snum; //if(ithread) cout<<"getting start frame number"<push(wbuffer)); return; @@ -2719,7 +2707,8 @@ void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* w //get last frame number uint64_t finalLastFrameNumberToSave = 0; uint32_t pnum; - if(getFrameandPacketNumber(ithread, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS + ((numpackets - 1) * onePacketSize), finalLastFrameNumberToSave,pnum) == FAIL){ + uint32_t snum; + if(getFrameandPacketNumber(ithread, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS + ((numpackets - 1) * onePacketSize), finalLastFrameNumberToSave,pnum,snum) == FAIL){ //error in frame number sent by fpga while(!fifoFree[ithread]->push(wbuffer)); return; @@ -2809,9 +2798,8 @@ void UDPStandardImplementation::copyFrameToGui(int ithread, char* buffer, uint32 cprintf(GREEN,"Writing_Thread: CopyingFrame: Going to copy data\n"); #endif //ensure previous frame was processed - if(!ithread) cout<<"*** waiting for writerguisemiphore (copyfrmae)"<push(wbuffer)); return; @@ -2978,11 +2967,15 @@ void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer -int UDPStandardImplementation::getFrameandPacketNumber(int ithread, char* wbuffer, uint64_t &framenumber, uint32_t &packetnumber){ +int UDPStandardImplementation::getFrameandPacketNumber(int ithread, char* wbuffer, uint64_t &framenumber, uint32_t &packetnumber,uint32_t &subframenumber){ FILE_LOG(logDEBUG) << __AT__ << " called"; eiger_packet_footer_t* footer=0; + eiger_packet_header_t* e_header=0; jfrau_packet_header_t* header=0; + framenumber = 0; + packetnumber = 0; + subframenumber = 0; switch(myDetectorType){ @@ -2996,12 +2989,15 @@ int UDPStandardImplementation::getFrameandPacketNumber(int ithread, char* wbuffe return FAIL; } packetnumber = (*( (uint16_t*) footer->packetNumber)); + e_header = (eiger_packet_header_t*) (wbuffer); + subframenumber = *( (uint32_t*) e_header->subFrameNumber); #ifdef DEBUG4 - if(!ithread) cprintf(GREEN,"Writing_Thread %d: fnum:%lld pnum:%d FPGA_fnum:%d footeroffset:%d\n", + if(!ithread) cprintf(GREEN,"Writing_Thread %d: fnum:%lld pnum:%d FPGA_fnum:%d subfnum:%d footeroffset:%d\n", ithread, (long long int)framenumber, packetnumber, framenumber, + subframenumber, footerOffset); #endif framenumber += (startFrameIndex - 1); @@ -3051,9 +3047,9 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, uint64_t tempframenumber=-1; offset = endoffset; uint32_t pnum; - + uint32_t snum; //get last frame number - if(getFrameandPacketNumber(ithread, wbuffer + (endoffset-onePacketSize), tempframenumber,pnum) == FAIL){ + if(getFrameandPacketNumber(ithread, wbuffer + (endoffset-onePacketSize), tempframenumber,pnum,snum) == FAIL){ //error in frame number sent by fpga while(!fifoFree[ithread]->push(wbuffer)); return FAIL; @@ -3079,7 +3075,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, offset -= bigIncrements; if(offsetpush(wbuffer)); return FAIL; @@ -3087,7 +3083,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } if(offsetpush(wbuffer)); return FAIL; @@ -3095,7 +3091,7 @@ int UDPStandardImplementation::writeUptoFrameNumber(int ithread, char* wbuffer, } while(tempframenumberpush(wbuffer)); return FAIL; From bdcbdba2ab9307c89c20ec99b16d4dbb27c3e5b0 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Tue, 20 Sep 2016 10:42:49 +0200 Subject: [PATCH 25/48] done with eiger --- .../src/UDPStandardImplementation.cpp | 67 ++++++++++++------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 68f850c53..985cff7c3 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1684,7 +1684,7 @@ void UDPStandardImplementation::startDataCallback(){ int bufferoffset = 0; int size = 0; int offset = 0; - int currentfnum = -1; + int currentfnum = 0; uint64_t fnum = 0; uint32_t pnum = 0; uint32_t snum = 0; @@ -1695,7 +1695,7 @@ void UDPStandardImplementation::startDataCallback(){ void *context = zmq_ctx_new(); void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // create a publisher int val = -1; - //zmq_setsockopt(zmq_socket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket + zmq_setsockopt(zmqsocket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket zmq_bind(zmqsocket,hostName); // bind //let calling function know thread started and obtained current (after sockets created) if(!zmqThreadStarted) @@ -1708,6 +1708,7 @@ void UDPStandardImplementation::startDataCallback(){ int acquisitionIndex = -1; int frameIndex = -1; int subframeIndex = -1; + bool newFrame = false; /* inner loop - loop for each buffer */ //until mask reset (dummy pcaket got by writer) @@ -1721,17 +1722,33 @@ void UDPStandardImplementation::startDataCallback(){ //everything is done if(guiNumPackets[ithread] == dummyPacketValue){ - /**suing this in clientzmq_msg_more, - * in serve use zmq_msg_send (&message, sender, ZMQ_SNDMORE); and 0 for last packet, but better to check lengt*/ - /*if (checkJoinThread()){for different scans - break; - }*/ - /*send half frames from before if any */ - //send header + //sending previous half frames if any + if(newFrame){cout<<"dummy but something remaining"<= size) break; - //new frame - if(currentfnum==-1){ - currentfnum = fnum; + + //last packet of same frame + if(fnum == currentfnum && pnum == packetsPerFrame){ + memcpy(buffer+((pnum-1)*oneDataSize), latestData[ithread]+offset+8,oneDataSize); + offset+= onePacketSize; + //send header //update frame details frameIndex = fnum; acquisitionIndex = fnum - startAcquisitionIndex; if(dynamicRange == 32) subframeIndex = snum; - } - - //last packet - if(pnum == packetsPerFrame){ - memcpy(buffer+((pnum-1)*oneDataSize), latestData[ithread]+offset+8,oneDataSize); - offset+= onePacketSize; - //send header int len = sprintf(buf,jsonFmt,type,shape, acquisitionIndex, frameIndex, subframeIndex,completeFileName[ithread]); zmq_send(zmqsocket, buf,len, ZMQ_SNDMORE); //send data zmq_send(zmqsocket, buffer, oneframesize, 0); + newFrame = false; #ifdef DEBUG if(!ithread)cprintf(BLUE,"%d sent (last packet)\n",ithread); #endif + currentfnum++; //start clock after sending if(!frameToGuiFrequency){ randomSendNow = false; clock_gettime(CLOCK_REALTIME, &begin); } memset(buffer,0xFF,oneframesize); - currentfnum = -1; + } //same frame (not last) or next frame else { //next frame - if(fnum > currentfnum){ + while(fnum > currentfnum){ //send header + //update frame details + frameIndex = fnum; + acquisitionIndex = fnum - startAcquisitionIndex; + if(dynamicRange == 32) subframeIndex = snum; int len = sprintf(buf,jsonFmt,type,shape, acquisitionIndex, frameIndex, subframeIndex,completeFileName[ithread]); zmq_send(zmqsocket, buf,len, ZMQ_SNDMORE); //send data zmq_send(zmqsocket, buffer, oneframesize, 0); + newFrame = false; #ifdef DEBUG cprintf(BLUE,"%d sent (last packet of previous frame)\n",ithread); #endif + currentfnum++; //start clock after sending if(!frameToGuiFrequency){ randomSendNow = false; clock_gettime(CLOCK_REALTIME, &begin); } memset(buffer,0xFF,oneframesize); - currentfnum = fnum; } memcpy(buffer+((pnum-1)*oneDataSize), latestData[ithread]+offset+8,oneDataSize); offset+= onePacketSize; + newFrame = true; } } From d6ca7ecbc4ba5c15d87b881102f6b5fe4f059227 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Tue, 20 Sep 2016 15:12:26 +0200 Subject: [PATCH 26/48] done for eiger, some checks for frameindex=-1,socket closing earlier than last socket etc --- slsReceiverSoftware/include/genericSocket.h | 2 +- .../src/UDPStandardImplementation.cpp | 95 +++++++++++-------- .../src/slsReceiverTCPIPInterface.cpp | 2 +- 3 files changed, 60 insertions(+), 39 deletions(-) diff --git a/slsReceiverSoftware/include/genericSocket.h b/slsReceiverSoftware/include/genericSocket.h index 11828c8a7..91debdec1 100644 --- a/slsReceiverSoftware/include/genericSocket.h +++ b/slsReceiverSoftware/include/genericSocket.h @@ -619,7 +619,7 @@ enum communicationProtocol{ nsent = recvfrom(socketDescriptor,(char*)buf+total_sent,nsending, 0, (struct sockaddr *) &clientAddress, &clientAddress_length); if(nsent < packet_size) { if(nsent){ - if(nsent != header_packet_size) + if((nsent != header_packet_size) && (nsent != -1)) cprintf(RED,"Incomplete Packet size %d\n",nsent); } break; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 985cff7c3..e51d65def 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -164,7 +164,7 @@ void UDPStandardImplementation::initializeMembers(){ for(int i=0; i0) && (getFrameandPacketNumber(ithread, latestData[ithread]+offset, fnum, pnum,snum)==FAIL)){ offset+= onePacketSize; } + //if(!ithread) cout<< ithread <<" fnum:"<< fnum<<" pnum:"<= size) break; @@ -1791,11 +1798,14 @@ void UDPStandardImplementation::startDataCallback(){ //last packet of same frame if(fnum == currentfnum && pnum == packetsPerFrame){ +#ifdef DEBUG + oldpnum=0; +#endif memcpy(buffer+((pnum-1)*oneDataSize), latestData[ithread]+offset+8,oneDataSize); offset+= onePacketSize; //send header //update frame details - frameIndex = fnum; + frameIndex = fnum;if(frameIndex==-1) cprintf(RED,"frameindex = -1, 222\n"); acquisitionIndex = fnum - startAcquisitionIndex; if(dynamicRange == 32) subframeIndex = snum; int len = sprintf(buf,jsonFmt,type,shape, acquisitionIndex, frameIndex, subframeIndex,completeFileName[ithread]); @@ -1818,10 +1828,20 @@ void UDPStandardImplementation::startDataCallback(){ //same frame (not last) or next frame else { //next frame +#ifdef DEBUG + int once = true; +#endif while(fnum > currentfnum){ +#ifdef DEBUG + if(once){ + if((fnum-currentfnum-1)>1) cprintf(RED,"%d Complete sub image missing:%d (cfnum:%d nfnum:%d)\n", + ithread,fnum-currentfnum-1,currentfnum,fnum); + once = false; + } +#endif //send header //update frame details - frameIndex = fnum; + frameIndex = fnum;if(frameIndex==-1) cprintf(RED,"frameindex = -1, 333\n"); acquisitionIndex = fnum - startAcquisitionIndex; if(dynamicRange == 32) subframeIndex = snum; int len = sprintf(buf,jsonFmt,type,shape, acquisitionIndex, frameIndex, subframeIndex,completeFileName[ithread]); @@ -2160,10 +2180,11 @@ void UDPStandardImplementation::stopListening(int ithread, int numbytes){ //reset mask and exit loop pthread_mutex_lock(&statusMutex); listeningThreadsMask^=(1<= (packetsPerFrame/numberofListeningThreads)) - if(dataStreamEnable && npackets) + if(dataStreamEnable && npackets > 0) copyFrameToGui(ithread, wbuffer,npackets); #ifdef DEBUG4 cprintf(GREEN,"Writing_Thread: Copied frame\n"); diff --git a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp index ff39c681b..822eb7fa6 100644 --- a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp +++ b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp @@ -2163,7 +2163,7 @@ int slsReceiverTCPIPInterface::set_data_stream_enable(){ strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } - else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + else if((index >= 0) && (receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING)){ strcpy(mess,"Can not set data stream enable while receiver not idle\n"); cprintf(RED,"%s\n",mess); ret = FAIL; From 1aff36efb8d18ee155583e6ffdbad2c716db5da5 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Wed, 21 Sep 2016 16:37:52 +0200 Subject: [PATCH 27/48] small print out change --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index e51d65def..d46ca831f 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1254,7 +1254,7 @@ int UDPStandardImplementation::createDataCallbackThreads(bool destroy){ zmqThreadStarted = false; currentThreadIndex = i; if(pthread_create(&dataCallbackThreads[i], NULL,startDataCallbackThread, (void*) this)){ - FILE_LOG(logERROR) << "Could not create listening thread with index " << i; + FILE_LOG(logERROR) << "Could not create data call back thread with index " << i; return FAIL; } while(!zmqThreadStarted); From 44870480be2f33be577e623fc64fd499238bb539 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Thu, 22 Sep 2016 13:17:17 +0200 Subject: [PATCH 28/48] merging for gotthard, not done --- slsReceiverSoftware/include/receiver_defs.h | 4 ++- .../src/UDPStandardImplementation.cpp | 27 ++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/slsReceiverSoftware/include/receiver_defs.h b/slsReceiverSoftware/include/receiver_defs.h index 0c60b892c..d0cd07320 100755 --- a/slsReceiverSoftware/include/receiver_defs.h +++ b/slsReceiverSoftware/include/receiver_defs.h @@ -169,7 +169,9 @@ typedef struct { #define EIGER_MAX_PORTS 2 -#define EIGER_HEADER_LENGTH 48 +#define EIGER_HEADER_PACKET_LENGTH 48 + +#define EIGER_HEADER_SIZE 8 #define EIGER_FIFO_SIZE 100 /*#define EIGER_ALIGNED_FRAME_SIZE 65536*/ diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index d46ca831f..01bf81401 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1219,7 +1219,7 @@ void UDPStandardImplementation::closeFile(int ithread){ int UDPStandardImplementation::createDataCallbackThreads(bool destroy){ FILE_LOG(logDEBUG) << __AT__ << " starting"; - + if(!destroy) cprintf(MAGENTA,"Data Callback thread created\n"); else cprintf(MAGENTA,"Data Callback thread destroyed\n"); //reset masks killAllDataCallbackThreads = false; pthread_mutex_lock(&statusMutex); @@ -1270,7 +1270,7 @@ int UDPStandardImplementation::createDataCallbackThreads(bool destroy){ int UDPStandardImplementation::createListeningThreads(bool destroy){ FILE_LOG(logDEBUG) << __AT__ << " starting"; - + if(!destroy) cprintf(BLUE,"Listening thread created\n"); else cprintf(BLUE,"Listening thread destroyed\n"); //reset masks killAllListeningThreads = false; pthread_mutex_lock(&statusMutex); @@ -1321,7 +1321,7 @@ int UDPStandardImplementation::createListeningThreads(bool destroy){ int UDPStandardImplementation::createWriterThreads(bool destroy){ FILE_LOG(logDEBUG) << __AT__ << " starting"; - + if(!destroy) cprintf(GREEN,"Writer thread created\n"); else cprintf(GREEN,"Writer thread destroyed\n"); //reset masks killAllWritingThreads = false; pthread_mutex_lock(&statusMutex); @@ -1439,7 +1439,7 @@ int UDPStandardImplementation::createUDPSockets(){ shutDownUDPSockets(); int headerpacketsize = 0; if(myDetectorType == EIGER) - headerpacketsize = EIGER_HEADER_LENGTH; + headerpacketsize = EIGER_HEADER_PACKET_LENGTH; //if no eth, listen to all if(!strlen(eth)){ @@ -1668,6 +1668,15 @@ void UDPStandardImplementation::startDataCallback(){ sprintf(hostName,"%s%d",hostName,portno); FILE_LOG(logINFO) << "Thread" << ithread << ": ZMQ Server at " << hostName; + + int headersize=0; + switch(myDetectorType){ + case EIGER: + headersize = EIGER_HEADER_SIZE; break; + + } + + /* outer loop - loops once for each acquisition */ //infinite loop, exited only to change dynamic range, 10G parameters etc (then recreated again) while(true){ @@ -1705,7 +1714,7 @@ void UDPStandardImplementation::startDataCallback(){ int frameIndex = -1; int subframeIndex = -1; #ifdef DEBUG - int oldpnum = 0; + int oldpnum = -1; #endif int datapacketscaught = 0; @@ -1797,11 +1806,11 @@ void UDPStandardImplementation::startDataCallback(){ //last packet of same frame - if(fnum == currentfnum && pnum == packetsPerFrame){ + if(fnum == currentfnum && pnum == (packetsPerFrame-1)){ #ifdef DEBUG oldpnum=0; #endif - memcpy(buffer+((pnum-1)*oneDataSize), latestData[ithread]+offset+8,oneDataSize); + memcpy(buffer+(pnum*oneDataSize), latestData[ithread]+offset+headersize,oneDataSize); offset+= onePacketSize; //send header //update frame details @@ -1861,7 +1870,7 @@ void UDPStandardImplementation::startDataCallback(){ memset(buffer,0xFF,oneframesize); } - memcpy(buffer+((pnum-1)*oneDataSize), latestData[ithread]+offset+8,oneDataSize); + memcpy(buffer+(pnum*oneDataSize), latestData[ithread]+offset+headersize,oneDataSize); offset+= onePacketSize; newFrame = true; } @@ -3030,7 +3039,7 @@ int UDPStandardImplementation::getFrameandPacketNumber(int ithread, char* wbuffe FILE_LOG(logERROR) << "Fifo "<< ithread << ": Frame Number is zero from firmware."; return FAIL; } - packetnumber = (*( (uint16_t*) footer->packetNumber)); + packetnumber = (*( (uint16_t*) footer->packetNumber))-1; e_header = (eiger_packet_header_t*) (wbuffer); subframenumber = *( (uint32_t*) e_header->subFrameNumber); #ifdef DEBUG4 From 489b623afd4c3dd9b7effb41f0db5e66092f8e8f Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Wed, 5 Oct 2016 08:24:35 +0200 Subject: [PATCH 29/48] trying to get in changes for activate in receiver --- .../include/UDPBaseImplementation.h | 15 +++ slsReceiverSoftware/include/UDPInterface.h | 14 +++ .../include/UDPStandardImplementation.h | 5 + .../include/slsReceiverTCPIPInterface.h | 4 + .../include/sls_receiver_funcs.h | 1 + .../src/UDPBaseImplementation.cpp | 14 ++- .../src/UDPStandardImplementation.cpp | 115 ++++++++++++++---- .../src/slsReceiverTCPIPInterface.cpp | 72 +++++++++++ 8 files changed, 212 insertions(+), 28 deletions(-) diff --git a/slsReceiverSoftware/include/UDPBaseImplementation.h b/slsReceiverSoftware/include/UDPBaseImplementation.h index f576e2666..8af8b8fa0 100644 --- a/slsReceiverSoftware/include/UDPBaseImplementation.h +++ b/slsReceiverSoftware/include/UDPBaseImplementation.h @@ -200,6 +200,13 @@ class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInter */ runStatus getStatus() const; + /** + * Get activate + * If deactivated, receiver will write dummy packets 0xFF + * (as it will receive nothing from detector) + * @return 0 for deactivated, 1 for activated + */ + int getActivate() const; @@ -432,6 +439,12 @@ class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInter */ void closeFile(int ithread = 0); + /** + * Activate / Deactivate Receiver + * If deactivated, receiver will write dummy packets 0xFF + * (as it will receive nothing from detector) + */ + int setActivate(int enable = -1); //***callback functions*** /** @@ -500,6 +513,8 @@ class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInter const static int MAX_NUMBER_OF_LISTENING_THREADS = 2; /** Receiver Status */ runStatus status; + /** Activated/Deactivated */ + int activated; //***connection parameters*** /** Ethernet Interface */ diff --git a/slsReceiverSoftware/include/UDPInterface.h b/slsReceiverSoftware/include/UDPInterface.h index 9ef30c680..0afda8d67 100644 --- a/slsReceiverSoftware/include/UDPInterface.h +++ b/slsReceiverSoftware/include/UDPInterface.h @@ -258,6 +258,13 @@ class UDPInterface { */ virtual slsReceiverDefs::runStatus getStatus() const = 0; + /** + * Get activate + * If deactivated, receiver will write dummy packets 0xFF + * (as it will receive nothing from detector) + * @return 0 for deactivated, 1 for activated + */ + virtual int getActivate() const = 0; @@ -489,6 +496,13 @@ class UDPInterface { virtual void closeFile(int ithread = 0) = 0; + /** + * Activate / Deactivate Receiver + * If deactivated, receiver will write dummy packets 0xFF + * (as it will receive nothing from detector) + */ + virtual int setActivate(int enable = -1) = 0; + //***callback functions*** /** * Call back for start acquisition diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index 14e0f638f..6e687b856 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -663,6 +663,7 @@ private: /** Missing Packet identifier value */ const static uint16_t missingPacketValue = 0xFFFF; + const static uint16_t deactivatedPacketValue = 0xFEFE; /** Dummy Packet identifier value */ const static uint32_t dummyPacketValue = 0xFFFFFFFF; @@ -756,6 +757,10 @@ private: + //***deactivated parameters*** + uint64_t deactivatedFrameNumber[MAX_NUMBER_OF_LISTENING_THREADS]; + int deactivatedFrameIncrement; + //***filter parameters*** diff --git a/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h b/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h index 8b30be84f..3deaac0a8 100644 --- a/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h +++ b/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h @@ -210,6 +210,10 @@ private: /** set fifo depth */ int set_fifo_depth(); + /** activate/ deactivate */ + int set_activate(); + + //General Functions /** Locks Receiver */ int lock_receiver(); diff --git a/slsReceiverSoftware/include/sls_receiver_funcs.h b/slsReceiverSoftware/include/sls_receiver_funcs.h index cc3490c42..6294a13ab 100644 --- a/slsReceiverSoftware/include/sls_receiver_funcs.h +++ b/slsReceiverSoftware/include/sls_receiver_funcs.h @@ -51,6 +51,7 @@ enum { F_ENABLE_RECEIVER_TEN_GIGA, /**< enable 10Gbe in receiver */ F_SET_RECEIVER_FIFO_DEPTH, /**< set receiver fifo depth */ + F_ACTIVATE, /** < activate/deactivate readout */ F_STREAM_DATA_FROM_RECEIVER /**< stream data from receiver to client */ /* Always append functions hereafter!!! */ diff --git a/slsReceiverSoftware/src/UDPBaseImplementation.cpp b/slsReceiverSoftware/src/UDPBaseImplementation.cpp index 4032f27e8..22c24e05d 100644 --- a/slsReceiverSoftware/src/UDPBaseImplementation.cpp +++ b/slsReceiverSoftware/src/UDPBaseImplementation.cpp @@ -49,6 +49,7 @@ void UDPBaseImplementation::initializeMembers(){ //***receiver parameters*** status = IDLE; + activated = true; //***connection parameters*** strcpy(eth,""); @@ -190,7 +191,7 @@ uint32_t UDPBaseImplementation::getFifoDepth() const{ FILE_LOG(logDEBUG) << __AT /***receiver status***/ slsReceiverDefs::runStatus UDPBaseImplementation::getStatus() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return status;} - +int UDPBaseImplementation::getActivate() const{FILE_LOG(logDEBUG) << __AT__ << " starting"; return activated;} /************************************************************************* @@ -464,6 +465,17 @@ void UDPBaseImplementation::closeFile(int ithread){ } +int UDPBaseImplementation::setActivate(int enable){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + if(enable != -1){ + activated = enable; + FILE_LOG(logINFO) << "Activation: " << stringEnable(activated); + } + + return activated; +} + /***callback functions***/ void UDPBaseImplementation::registerCallBackStartAcquisition(int (*func)(char*, char*,int, int, void*),void *arg){ startAcquisitionCallBack=func; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 01bf81401..cd0ac82b3 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -212,6 +212,12 @@ void UDPStandardImplementation::initializeMembers(){ createFileMask = 0x0; killAllWritingThreads = false; + + //***deactivated parameters*** + for(int i=0; i < MAX_NUMBER_OF_LISTENING_THREADS; i++) + deactivatedFrameNumber[i] = 0; + deactivatedFrameIncrement = 0; + //***filter parameters*** commonModeSubtractionEnable = false; moenchCommonModeSubtraction = NULL; @@ -957,6 +963,11 @@ int UDPStandardImplementation::startReceiver(char *c){ fileCreateSuccess = false; pthread_mutex_unlock(&statusMutex); + //deactivated parameters + for(int i = 0; i < numberofListeningThreads; ++i) + deactivatedFrameNumber[i] = 0; + deactivatedFrameIncrement = (bufferSize/(onePacketSize*packetsPerFrame))*numberofJobsPerBuffer; + FILE_LOG(logINFO) << "Deactivated Frame Increment:" << deactivatedFrameIncrement; //Print Receiver Configuration @@ -1095,44 +1106,48 @@ void UDPStandardImplementation::startReadout(){ if(status == RUNNING){ - //check if all packets got - int totalP = 0,prev=-1,i; - for(i=0; igetCurrentTotalReceived(); + //needs to wait for packets only if activated + if(activated){ + //check if all packets got + int totalP = 0,prev=-1,i; + for(i=0; igetCurrentTotalReceived(); - //wait as long as there is change from prev totalP, - //and also change from received in buffer to previous value - //(as one listens to many at a time, shouldnt cut off in between) - while((prev != totalP) || (prevReceivedInBuffer!= currentReceivedInBuffer)){ + //wait for all packets + if(totalP!=numberOfFrames*packetsPerFrame*numberofListeningThreads){ + + //wait as long as there is change from prev totalP, + //and also change from received in buffer to previous value + //(as one listens to many at a time, shouldnt cut off in between) + while((prev != totalP) || (prevReceivedInBuffer!= currentReceivedInBuffer)){ #ifdef DEBUG5 - cprintf(MAGENTA,"waiting for all packets totalP:%d currently in buffer:%d\n",totalP,currentReceivedInBuffer); + cprintf(MAGENTA,"waiting for all packets totalP:%d currently in buffer:%d\n",totalP,currentReceivedInBuffer); #endif - usleep(5000);/* Need to find optimal time (exposure time and acquisition period) **/ - prev = totalP; - totalP = 0; - for(i=0; igetCurrentTotalReceived(); + prevReceivedInBuffer = currentReceivedInBuffer; + currentReceivedInBuffer = 0; + for(i=0; igetCurrentTotalReceived(); #ifdef DEBUG5 - cprintf(MAGENTA,"\tupdated: totalP:%d currently in buffer:%d\n",totalP,currentReceivedInBuffer); + cprintf(MAGENTA,"\tupdated: totalP:%d currently in buffer:%d\n",totalP,currentReceivedInBuffer); #endif + } + } - } //set status @@ -1953,7 +1968,7 @@ void UDPStandardImplementation::startListening(){ //udpsocket doesnt exist - if(status == TRANSMITTING){ + if ((status == TRANSMITTING)||(rc == 0 && activated == 0)){ FILE_LOG(logERROR) << "Listening_Thread " << ithread << ": UDP Socket not created or shut down earlier"; stopListening(ithread,0); continue; @@ -2023,6 +2038,50 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, ch //carry over from previous buffer if(cSize) memcpy(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS, temp, cSize); + + if(!activated){ + //cSize = 0 for deactivated + int framestoclone = 0; + //done + if(deactivatedFrameNumber[ithread] == numberOfFrames) + return 0; + //last + if((deactivatedFrameNumber[ithread] + deactivatedFrameIncrement) > numberOfFrames) + framestoclone = numberOfFrames - deactivatedFrameNumber[ithread]; + //in progress + else + framestoclone = deactivatedFrameIncrement; + + //copy dummy packets + memset(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS, 0xFF,framestoclone*packetsPerFrame*onePacketSize); + + //set fnum, pnum and deactivatedpacket label + eiger_packet_header_t* header; + eiger_packet_footer_t* footer; + int pnum=0; + //loop by each packet + for(int offset=HEADER_SIZE_NUM_TOT_PACKETS; + offsetpacketNumber) = ++pnum; + *( (uint16_t*) header->missingPacket) = deactivatedPacketValue; +#ifdef MANUALDEBUG + cprintf(GREEN,"thread:%d pnum:%d fnum:%d\n", + ithread, + (*( (uint16_t*) footer->packetNumber)), + (uint32_t)(*( (uint64_t*) footer))); +#endif + if(pnum == packetsPerFrame) + pnum = 0; + } + + return framestoclone*onePacketSize; + } + + if(status != TRANSMITTING) receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + cSize, (bufferSize * numberofJobsPerBuffer) - cSize); //eiger returns 0 when header packet caught @@ -2604,6 +2663,8 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ } } + if(!activated) + cprintf(RED,"Note: Deactivated Receiver\n"); //acquisition end if (acquisitionFinishedCallBack) diff --git a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp index 607b00e14..9b7518e95 100644 --- a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp +++ b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp @@ -262,6 +262,7 @@ int slsReceiverTCPIPInterface::function_table(){ flist[F_ENABLE_RECEIVER_TEN_GIGA] = &slsReceiverTCPIPInterface::enable_tengiga; flist[F_SET_RECEIVER_FIFO_DEPTH] = &slsReceiverTCPIPInterface::set_fifo_depth; + flist[F_ACTIVATE] = &slsReceiverTCPIPInterface::set_activate; flist[F_STREAM_DATA_FROM_RECEIVER] = &slsReceiverTCPIPInterface::set_data_stream_enable; #ifdef VERYVERBOSE @@ -2862,6 +2863,77 @@ int slsReceiverTCPIPInterface::set_fifo_depth() { +int slsReceiverTCPIPInterface::set_activate() { + ret=OK; + int retval=-1; + int enable; + strcpy(mess,"Could not activate/deactivate\n"); + + + // receive arguments + if(socket->ReceiveDataOnly(&enable,sizeof(enable)) < 0 ){ + strcpy(mess,"Error reading from socket\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + if (ret==OK) { + if (lockStatus==1 && socket->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", socket->lastClientIP); + ret=FAIL; + } + + if(ret!=FAIL){ + if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + cprintf(RED,"%s",mess); + ret=FAIL; + }else if(receiverBase->getStatus()==RUNNING){ + strcpy(mess,"Cannot activate/deactivate while status is running\n"); + cprintf(RED,"%s",mess); + ret=FAIL; + }else{ + if(enable != -1) + receiverBase->setActivate(enable); + retval = receiverBase->getActivate(); + if(enable >= 0 && retval != enable){ + sprintf(mess,"Tried to set activate to %d, but returned %d\n",enable,retval); + ret = FAIL; + cprintf(RED,"%s",mess); + } + } + } + } +#endif +#ifdef VERYVERBOSE + if(ret!=FAIL) + cout << "Activate: " << retval << endl; + else + cout << mess << endl; +#endif + + + if(ret==OK && socket->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + socket->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + socket->SendDataOnly(mess,sizeof(mess)); + } + socket->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + From bf54c1556072c43fe25f64be750f0579616a01a9 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Wed, 5 Oct 2016 09:30:08 +0200 Subject: [PATCH 30/48] updated to have activate function implemented --- .../src/UDPStandardImplementation.cpp | 47 ++++++++++++------- .../src/slsReceiverTCPIPInterface.cpp | 2 +- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index cd0ac82b3..cf126b771 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -97,6 +97,10 @@ void UDPStandardImplementation::deleteMembers(){ createWriterThreads(true); threadStarted = false; } + if(zmqThreadStarted){ + createDataCallbackThreads(true); + zmqThreadStarted = false; + } } void UDPStandardImplementation::deleteFilter(){ @@ -452,7 +456,6 @@ void UDPStandardImplementation::setFileName(const char c[]){ detID = 0; } - if(dataStreamEnable && (strcmp(oldfilename,fileName))){ if(zmqThreadStarted) createDataCallbackThreads(true); @@ -576,6 +579,8 @@ uint32_t UDPStandardImplementation::setDataStreamEnable(const uint32_t enable){ int olddatasend = dataStreamEnable; dataStreamEnable = enable; + FILE_LOG(logINFO) << "Data Send to Gui: " << dataStreamEnable; + //if there is a change if(olddatasend != dataStreamEnable){ if(zmqThreadStarted) @@ -589,9 +594,6 @@ uint32_t UDPStandardImplementation::setDataStreamEnable(const uint32_t enable){ } } - FILE_LOG(logINFO) << "Data Send to Gui: " << dataStreamEnable; - - return OK; } @@ -1968,7 +1970,7 @@ void UDPStandardImplementation::startListening(){ //udpsocket doesnt exist - if ((status == TRANSMITTING)||(rc == 0 && activated == 0)){ + if(activated && udpSocket[ithread] == NULL){ FILE_LOG(logERROR) << "Listening_Thread " << ithread << ": UDP Socket not created or shut down earlier"; stopListening(ithread,0); continue; @@ -1981,7 +1983,7 @@ void UDPStandardImplementation::startListening(){ if((!measurementStarted) && (rc > 0)) startFrameIndices(ithread); //problem in receiving or end of acquisition - if (status == TRANSMITTING){ + if (status == TRANSMITTING||(rc == 0 && activated == 0)){ stopListening(ithread,rc); continue; } @@ -2040,20 +2042,25 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, ch if(!activated){ + //cSize = 0 for deactivated int framestoclone = 0; + //first + if(deactivatedFrameNumber[ithread]==0) + deactivatedFrameNumber[ithread]++; //done - if(deactivatedFrameNumber[ithread] == numberOfFrames) + if(deactivatedFrameNumber[ithread] == (numberOfFrames+1)) return 0; //last - if((deactivatedFrameNumber[ithread] + deactivatedFrameIncrement) > numberOfFrames) - framestoclone = numberOfFrames - deactivatedFrameNumber[ithread]; + if((deactivatedFrameNumber[ithread] + deactivatedFrameIncrement) > (numberOfFrames+1)) + framestoclone = (numberOfFrames+1) - deactivatedFrameNumber[ithread]; //in progress else framestoclone = deactivatedFrameIncrement; //copy dummy packets - memset(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS, 0xFF,framestoclone*packetsPerFrame*onePacketSize); + receivedSize = framestoclone*packetsPerFrame*onePacketSize; + memset(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS, 0xFF,receivedSize); //set fnum, pnum and deactivatedpacket label eiger_packet_header_t* header; @@ -2061,24 +2068,28 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, ch int pnum=0; //loop by each packet for(int offset=HEADER_SIZE_NUM_TOT_PACKETS; - offsetpacketNumber) = ++pnum; *( (uint16_t*) header->missingPacket) = deactivatedPacketValue; #ifdef MANUALDEBUG - cprintf(GREEN,"thread:%d pnum:%d fnum:%d\n", - ithread, - (*( (uint16_t*) footer->packetNumber)), - (uint32_t)(*( (uint64_t*) footer))); + if(!ithread){ + cprintf(GREEN,"thread:%d pnum:%d fnum:%d\n", + ithread, + (*( (uint16_t*) footer->packetNumber)), + (uint32_t)(*( (uint64_t*) footer))); + } #endif - if(pnum == packetsPerFrame) + if(pnum == packetsPerFrame){ pnum = 0; + deactivatedFrameNumber[ithread]++; + } } - return framestoclone*onePacketSize; + return receivedSize; } diff --git a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp index 9b7518e95..c99df4f36 100644 --- a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp +++ b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp @@ -877,7 +877,7 @@ int slsReceiverTCPIPInterface::stop_receiver(){ ret=FAIL; } else{ - if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING || receiverBase->getStatus()==RUN_FINISHED){ receiverBase->stopReceiver(); } s = receiverBase->getStatus(); From 09e8bf414458505dc13bad914ad784fd8cbc5666 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Wed, 5 Oct 2016 15:27:25 +0200 Subject: [PATCH 31/48] somewhere --- .../src/UDPStandardImplementation.cpp | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index cf126b771..996e708b9 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -95,12 +95,9 @@ void UDPStandardImplementation::deleteMembers(){ if(threadStarted){ createListeningThreads(true); createWriterThreads(true); - threadStarted = false; } - if(zmqThreadStarted){ + if(zmqThreadStarted) createDataCallbackThreads(true); - zmqThreadStarted = false; - } } void UDPStandardImplementation::deleteFilter(){ @@ -338,7 +335,6 @@ int UDPStandardImplementation::setupFifoStructure(){ if(threadStarted){ createListeningThreads(true); createWriterThreads(true); - threadStarted = false; } @@ -456,16 +452,6 @@ void UDPStandardImplementation::setFileName(const char c[]){ detID = 0; } - if(dataStreamEnable && (strcmp(oldfilename,fileName))){ - if(zmqThreadStarted) - createDataCallbackThreads(true); - numberofDataCallbackThreads = MAX_NUMBER_OF_LISTENING_THREADS; - if(createDataCallbackThreads() == FAIL){ - cprintf(BG_RED,"Error: Could not create data callback threads\n"); - } - } - - FILE_LOG(logINFO) << "File name:" << fileName; } @@ -576,21 +562,17 @@ int UDPStandardImplementation::setFrameToGuiFrequency(const uint32_t freq){ uint32_t UDPStandardImplementation::setDataStreamEnable(const uint32_t enable){ FILE_LOG(logDEBUG) << __AT__ << " called"; - - int olddatasend = dataStreamEnable; dataStreamEnable = enable; FILE_LOG(logINFO) << "Data Send to Gui: " << dataStreamEnable; - //if there is a change - if(olddatasend != dataStreamEnable){ - if(zmqThreadStarted) - createDataCallbackThreads(true); + //data sockets have to be created again as the client ones are + if(zmqThreadStarted) + createDataCallbackThreads(true); - if(dataStreamEnable){ - numberofDataCallbackThreads = MAX_NUMBER_OF_LISTENING_THREADS; - if(createDataCallbackThreads() == FAIL){ - cprintf(BG_RED,"Error: Could not create data callback threads\n"); - } + if(dataStreamEnable){ + numberofDataCallbackThreads = MAX_NUMBER_OF_LISTENING_THREADS; + if(createDataCallbackThreads() == FAIL){ + cprintf(BG_RED,"Error: Could not create data callback threads\n"); } } @@ -1690,7 +1672,8 @@ void UDPStandardImplementation::startDataCallback(){ switch(myDetectorType){ case EIGER: headersize = EIGER_HEADER_SIZE; break; - + default: + headersize = 0; break; } From 65acd118c5d190f6e317dbb33ce760560ab6443d Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Thu, 6 Oct 2016 13:51:23 +0200 Subject: [PATCH 32/48] looks done --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 996e708b9..eb099aaf8 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1665,7 +1665,7 @@ void UDPStandardImplementation::startDataCallback(){ char hostName[100] = "tcp://127.0.0.1:"; int portno = DEFAULT_ZMQ_PORTNO + (detID*2+ithread); sprintf(hostName,"%s%d",hostName,portno); - FILE_LOG(logINFO) << "Thread" << ithread << ": ZMQ Server at " << hostName; + int headersize=0; @@ -1700,6 +1700,7 @@ void UDPStandardImplementation::startDataCallback(){ int val = -1; zmq_setsockopt(zmqsocket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket zmq_bind(zmqsocket,hostName); // bind + FILE_LOG(logINFO) << "Thread" << ithread << ": ZMQ Server at " << hostName; //let calling function know thread started and obtained current (after sockets created) if(!zmqThreadStarted) From 6f6199ad204de56c62f18a381756a6a59735fdb0 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Thu, 6 Oct 2016 14:43:56 +0200 Subject: [PATCH 33/48] moved created socket outside innner and outer loop --- .../src/UDPStandardImplementation.cpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index eb099aaf8..317eadd25 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1666,6 +1666,13 @@ void UDPStandardImplementation::startDataCallback(){ int portno = DEFAULT_ZMQ_PORTNO + (detID*2+ithread); sprintf(hostName,"%s%d",hostName,portno); + //socket details + void *context = zmq_ctx_new(); + void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // create a publisher + int val = -1; + zmq_setsockopt(zmqsocket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket + zmq_bind(zmqsocket,hostName); // bind + FILE_LOG(logINFO) << "Thread" << ithread << ": ZMQ Server at " << hostName; int headersize=0; @@ -1676,6 +1683,9 @@ void UDPStandardImplementation::startDataCallback(){ headersize = 0; break; } + //let calling function know thread started and obtained current (after sockets created) + if(!zmqThreadStarted) + zmqThreadStarted = true; /* outer loop - loops once for each acquisition */ //infinite loop, exited only to change dynamic range, 10G parameters etc (then recreated again) @@ -1694,17 +1704,7 @@ void UDPStandardImplementation::startDataCallback(){ bool randomSendNow = true; bool newFrame = false; - //socket details - void *context = zmq_ctx_new(); - void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // create a publisher - int val = -1; - zmq_setsockopt(zmqsocket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket - zmq_bind(zmqsocket,hostName); // bind - FILE_LOG(logINFO) << "Thread" << ithread << ": ZMQ Server at " << hostName; - //let calling function know thread started and obtained current (after sockets created) - if(!zmqThreadStarted) - zmqThreadStarted = true; //header details const char *type = "float64"; @@ -1884,24 +1884,24 @@ void UDPStandardImplementation::startDataCallback(){ //free resources delete[] buffer; - zmq_unbind(zmqsocket, hostName); /* will this be too soon and cut the sending*/ - zmq_close(zmqsocket); - zmq_ctx_destroy(context); //end of acquisition, wait for next acquisition/change of parameters sem_wait(&dataCallbackSemaphore[ithread]); - - //check to exit thread (for change of parameters) - only EXIT possibility if(killAllDataCallbackThreads){ - cprintf(MAGENTA,"DataCallback_Thread %d:Goodbye!\n",ithread); - pthread_exit(NULL); + break;//pthread_exit(NULL); } }/*--end of loop for each acquisition (outer loop) */ + + //free resources + zmq_unbind(zmqsocket, hostName); /* will this be too soon and cut the sending*/ + zmq_close(zmqsocket); + zmq_ctx_destroy(context); + cprintf(MAGENTA,"DataCallback_Thread %d:Goodbye!\n",ithread); } From e6db70354ce16d1498a59ad2e7a72660d86d0eda Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Thu, 6 Oct 2016 15:50:11 +0200 Subject: [PATCH 34/48] thread starting if rxr closed and others --- .../src/UDPStandardImplementation.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 317eadd25..e4113089f 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -562,17 +562,21 @@ int UDPStandardImplementation::setFrameToGuiFrequency(const uint32_t freq){ uint32_t UDPStandardImplementation::setDataStreamEnable(const uint32_t enable){ FILE_LOG(logDEBUG) << __AT__ << " called"; + int oldvalue = dataStreamEnable; dataStreamEnable = enable; FILE_LOG(logINFO) << "Data Send to Gui: " << dataStreamEnable; - //data sockets have to be created again as the client ones are - if(zmqThreadStarted) - createDataCallbackThreads(true); - if(dataStreamEnable){ - numberofDataCallbackThreads = MAX_NUMBER_OF_LISTENING_THREADS; - if(createDataCallbackThreads() == FAIL){ - cprintf(BG_RED,"Error: Could not create data callback threads\n"); + if(oldvalue!=dataStreamEnable){ + //data sockets have to be created again as the client ones are + if(zmqThreadStarted) + createDataCallbackThreads(true); + + if(dataStreamEnable){ + numberofDataCallbackThreads = MAX_NUMBER_OF_LISTENING_THREADS; + if(createDataCallbackThreads() == FAIL){ + cprintf(BG_RED,"Error: Could not create data callback threads\n"); + } } } From 59f3aef703f4208d63479d8392e274c03844c0d1 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Fri, 7 Oct 2016 10:15:19 +0200 Subject: [PATCH 35/48] back to subscriber publisher --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index e4113089f..8d2ec4c6e 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1672,8 +1672,8 @@ void UDPStandardImplementation::startDataCallback(){ //socket details void *context = zmq_ctx_new(); - void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // create a publisher - int val = -1; + void *zmqsocket = zmq_socket(context, ZMQ_PUB); // create a publisher + int val = 100; zmq_setsockopt(zmqsocket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket zmq_bind(zmqsocket,hostName); // bind FILE_LOG(logINFO) << "Thread" << ithread << ": ZMQ Server at " << hostName; From 831bd8e160bf8d25ccd0972d28c85f6d527f614b Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Fri, 7 Oct 2016 11:40:56 +0200 Subject: [PATCH 36/48] some changes for quitting gui and continuing with acquire from command line --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 5 +++-- slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 8d2ec4c6e..4971cc589 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1672,8 +1672,8 @@ void UDPStandardImplementation::startDataCallback(){ //socket details void *context = zmq_ctx_new(); - void *zmqsocket = zmq_socket(context, ZMQ_PUB); // create a publisher - int val = 100; + void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // create a publisher + int val = -1; zmq_setsockopt(zmqsocket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket zmq_bind(zmqsocket,hostName); // bind FILE_LOG(logINFO) << "Thread" << ithread << ": ZMQ Server at " << hostName; @@ -1746,6 +1746,7 @@ void UDPStandardImplementation::startDataCallback(){ zmq_send(zmqsocket, buf,len, ZMQ_SNDMORE); //send data zmq_send(zmqsocket, buffer, oneframesize, 0); + cout<<"sent last dummy"< Date: Fri, 7 Oct 2016 12:14:08 +0200 Subject: [PATCH 37/48] removing check for read out --- slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp index aba6205a5..fd06334ab 100644 --- a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp +++ b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp @@ -2305,10 +2305,10 @@ int slsReceiverTCPIPInterface::start_readout(){cprintf(BLUE,"In start readout!\n strcpy(mess,SET_RECEIVER_ERR_MESSAGE); ret=FAIL; } - else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ + /*else if(receiverBase->getStatus()==RUNNING || receiverBase->getStatus()==TRANSMITTING){ strcpy(mess,"Can not start receiver readout while receiver not idle\n"); ret = FAIL; - } + }*/ else{ receiverBase->startReadout(); retval = receiverBase->getStatus(); From 47516cafdef86f005205d03afacd45e82f00f9a5 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Fri, 7 Oct 2016 14:26:53 +0200 Subject: [PATCH 38/48] works --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 4971cc589..2180bf2a0 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -2250,7 +2250,7 @@ void UDPStandardImplementation::stopListening(int ithread, int numbytes){ listeningThreadsMask^=(1< Date: Tue, 11 Oct 2016 12:35:49 +0200 Subject: [PATCH 39/48] somewhere --- slsReceiverSoftware/include/receiver_defs.h | 8 +++----- .../src/UDPStandardImplementation.cpp | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/slsReceiverSoftware/include/receiver_defs.h b/slsReceiverSoftware/include/receiver_defs.h index d0cd07320..8ecd46ba9 100755 --- a/slsReceiverSoftware/include/receiver_defs.h +++ b/slsReceiverSoftware/include/receiver_defs.h @@ -171,7 +171,6 @@ typedef struct { #define EIGER_MAX_PORTS 2 #define EIGER_HEADER_PACKET_LENGTH 48 -#define EIGER_HEADER_SIZE 8 #define EIGER_FIFO_SIZE 100 /*#define EIGER_ALIGNED_FRAME_SIZE 65536*/ @@ -182,7 +181,7 @@ typedef struct { #define EIGER_ONE_GIGA_ONE_DATA_SIZE 1024 #define EIGER_TEN_GIGA_ONE_PACKET_SIZE 4112 #define EIGER_TEN_GIGA_ONE_DATA_SIZE 4096 -#define EIGER_PACKET_HEADER_SIZE 8 +#define EIGER_DATA_PACKET_HEADER_SIZE 8 //#define EIGER_BUFFER_SIZE_CONSTANT (EIGER_ONE_PACKET_SIZE*EIGER_PACKETS_PER_FRAME_COSTANT)//1040*16*2//*bit mode //#define EIGER_DATA_BYTES_CONSTANT (EIGER_ONE_DATA_SIZE*EIGER_PACKETS_PER_FRAME_COSTANT) //1024*16*2//*bit mode @@ -190,9 +189,8 @@ typedef struct { #define EIGER_FRAME_INDEX_OFFSET 0 #define EIGER_PACKET_INDEX_MASK 0x0 -#define EIGER_IMAGE_HEADER_SIZE 48 - -#define EIGER_PIXELS_IN_ONE_ROW (256*4) +//for each thread +#define EIGER_PIXELS_IN_ONE_ROW (256*2) #define EIGER_PIXELS_IN_ONE_COL (256) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 2180bf2a0..667892f95 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -671,7 +671,7 @@ int UDPStandardImplementation::setTenGigaEnable(const bool b){ oneDataSize = EIGER_ONE_GIGA_ONE_DATA_SIZE; } bufferSize = onePacketSize * packetsPerFrame; - footerOffset = EIGER_PACKET_HEADER_SIZE + oneDataSize; + footerOffset = EIGER_DATA_PACKET_HEADER_SIZE + oneDataSize; FILE_LOG(logDEBUG) << dec << "packetsPerFrame:" << packetsPerFrame << "\nonePacketSize:" << onePacketSize << @@ -803,7 +803,7 @@ int UDPStandardImplementation::setDetectorType(const detectorType d){ maxFramesPerFile = EIGER_MAX_FRAMES_PER_FILE; fifoSize = EIGER_FIFO_SIZE; fifoDepth = EIGER_FIFO_SIZE; - footerOffset = EIGER_PACKET_HEADER_SIZE + oneDataSize; + footerOffset = EIGER_DATA_PACKET_HEADER_SIZE + oneDataSize; break; case JUNGFRAUCTB: packetsPerFrame = JCTB_PACKETS_PER_FRAME; @@ -1682,7 +1682,7 @@ void UDPStandardImplementation::startDataCallback(){ int headersize=0; switch(myDetectorType){ case EIGER: - headersize = EIGER_HEADER_SIZE; break; + headersize = EIGER_DATA_PACKET_HEADER_SIZE; break; default: headersize = 0; break; } @@ -2884,13 +2884,23 @@ void UDPStandardImplementation::updateFileHeader(int ithread){ while((unsigned int)length!=strlen(fileHeader[ithread])){ length = strlen(fileHeader[ithread]); sprintf(fileHeader[ithread],"\nHeader\t\t %d bytes\n" + "Top\t\t %d\n" + "Left\t\t %d\n" "Dynamic Range\t %d\n" + "Ten Giga\t %d\n" "Packet\t\t %d bytes\n" + "Data\t\t %d bytes\n" "x\t\t %d pixels\n" "y\t\t %d pixels\n" "Timestamp\t %s\n\n" "%s", - length,dynamicRange,onePacketSize,xpix,ypix,ctime(&t), + length, + (bottomEnable?0:1),(ithread?0:1), + dynamicRange,tengigaEnable, + onePacketSize,oneDataSize, + xpix,ypix, + + ctime(&t), packetheader); } From 5c4d55af6b27fce7523a679f514e32bec292ae09 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Wed, 12 Oct 2016 08:53:13 +0200 Subject: [PATCH 40/48] random read, the current fnum shoud be sent, not the padded frame --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 667892f95..f5df08f7b 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1810,6 +1810,9 @@ void UDPStandardImplementation::startDataCallback(){ if(offset >= size) break; + if(!frameToGuiFrequency) + currentfnum = fnum; + //last packet of same frame if(fnum == currentfnum && pnum == (packetsPerFrame-1)){ From b0bedc516d6d9f69b058003f8b2aefe5e646799a Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Fri, 14 Oct 2016 16:08:43 +0200 Subject: [PATCH 41/48] works for missing packets and image reconstruction --- .../include/UDPStandardImplementation.h | 9 +- .../src/UDPStandardImplementation.cpp | 94 +++++++++---------- 2 files changed, 48 insertions(+), 55 deletions(-) diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h index 6e687b856..7ec396ba0 100644 --- a/slsReceiverSoftware/include/UDPStandardImplementation.h +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -591,7 +591,9 @@ private: /** If file created successfully for all Writer Threads */ bool fileCreateSuccess; - char fileHeader[MAX_NUMBER_OF_WRITER_THREADS][MAX_STR_LENGTH]; + const static int FILE_HEADER_SIZE = 400; + + char fileHeader[MAX_NUMBER_OF_WRITER_THREADS][FILE_HEADER_SIZE]; @@ -661,9 +663,8 @@ private: /** Total fifo size */ uint32_t fifoSize; - /** Missing Packet identifier value */ - const static uint16_t missingPacketValue = 0xFFFF; - const static uint16_t deactivatedPacketValue = 0xFEFE; + /** Missing Packet */ + int missingPacketinFile; /** Dummy Packet identifier value */ const static uint32_t dummyPacketValue = 0xFFFFFFFF; diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index f5df08f7b..eee2f5373 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1586,7 +1586,7 @@ int UDPStandardImplementation::createNewFile(int ithread){ //write file header if(myDetectorType == EIGER) - fwrite((void*)fileHeader[ithread], 1, strlen(fileHeader[ithread]), sfilefd[ithread]); + fwrite((void*)fileHeader[ithread], 1, FILE_HEADER_SIZE, sfilefd[ithread]); } //reset counters for each new file @@ -1823,7 +1823,7 @@ void UDPStandardImplementation::startDataCallback(){ offset+= onePacketSize; //send header //update frame details - frameIndex = fnum;if(frameIndex==-1) cprintf(RED,"frameindex = -1, 222\n"); + frameIndex = fnum; acquisitionIndex = fnum - startAcquisitionIndex; if(dynamicRange == 32) subframeIndex = snum; int len = sprintf(buf,jsonFmt,type,shape, acquisitionIndex, frameIndex, subframeIndex,completeFileName[ithread]); @@ -1859,7 +1859,7 @@ void UDPStandardImplementation::startDataCallback(){ #endif //send header //update frame details - frameIndex = fnum;if(frameIndex==-1) cprintf(RED,"frameindex = -1, 333\n"); + frameIndex = fnum; acquisitionIndex = fnum - startAcquisitionIndex; if(dynamicRange == 32) subframeIndex = snum; int len = sprintf(buf,jsonFmt,type,shape, acquisitionIndex, frameIndex, subframeIndex,completeFileName[ithread]); @@ -2066,7 +2066,6 @@ int UDPStandardImplementation::prepareAndListenBuffer(int ithread, int cSize, ch footer = (eiger_packet_footer_t*)(buffer[ithread] + offset + footerOffset); *( (uint64_t*) footer) = deactivatedFrameNumber[ithread]; *( (uint16_t*) footer->packetNumber) = ++pnum; - *( (uint16_t*) header->missingPacket) = deactivatedPacketValue; #ifdef MANUALDEBUG if(!ithread){ cprintf(GREEN,"thread:%d pnum:%d fnum:%d\n", @@ -2613,6 +2612,12 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ //all threads need to close file, reset mask and exit loop + missingPacketinFile = (long long int)numberOfFrames*packetsPerFrame-totalWritingPacketCount[ithread]; + if(missingPacketinFile){ + updateFileHeader(ithread); + fseek(sfilefd[ithread],0,0); + fwrite((void*)fileHeader[ithread], 1, FILE_HEADER_SIZE, sfilefd[ithread]); + } closeFile(ithread); pthread_mutex_lock(&statusMutex); writerThreadsMask^=(1< FILE_HEADER_SIZE) + cprintf(BG_RED,"File Header Size is too small for file header\n"); - ctime(&t), - packetheader); - } } From 37c0ea74534921fe708f703e87bfca34c0971a9f Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Tue, 18 Oct 2016 08:43:21 +0200 Subject: [PATCH 42/48] almost there --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index eee2f5373..28d079009 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1673,8 +1673,10 @@ void UDPStandardImplementation::startDataCallback(){ //socket details void *context = zmq_ctx_new(); void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // create a publisher - int val = -1; + int val = 4; zmq_setsockopt(zmqsocket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket + //val = 10; + //zmq_setsockopt(zmqsocket,ZMQ_SNDHWM,&val,sizeof(val)); //set SEND HIGH WATER MARK (8-9ms slower) zmq_bind(zmqsocket,hostName); // bind FILE_LOG(logINFO) << "Thread" << ithread << ": ZMQ Server at " << hostName; @@ -1750,6 +1752,8 @@ void UDPStandardImplementation::startDataCallback(){ newFrame = false; } + + /* //send final header //update frame details #ifdef DEBUG @@ -1762,6 +1766,7 @@ void UDPStandardImplementation::startDataCallback(){ zmq_send(zmqsocket, buf,len, ZMQ_SNDMORE); //send final data zmq_send (zmqsocket, "end", 3, 0); + */ pthread_mutex_lock(&statusMutex); dataCallbackThreadsMask^=(1< Date: Tue, 18 Oct 2016 11:10:42 +0200 Subject: [PATCH 43/48] done --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 28d079009..7f811a830 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1785,7 +1785,7 @@ void UDPStandardImplementation::startDataCallback(){ cprintf(BLUE,"%d Elapsed time:%f seconds\n",ithread,( end.tv_sec - begin.tv_sec ) + ( end.tv_nsec - begin.tv_nsec ) / 1000000000.0); #endif //still less than 250 ms, keep waiting - if((( end.tv_sec - begin.tv_sec ) + ( end.tv_nsec - begin.tv_nsec ) / 1000000000.0) < 0.250)/**fixed 250 ms*/ + if((( end.tv_sec - begin.tv_sec ) + ( end.tv_nsec - begin.tv_nsec ) / 1000000000.0) < 0.5)/**fixed 250 ms*/ continue; //done with timer, look into data randomSendNow = true; From e00ad76e556a3449ba9c1790d87c7bffeff5d07d Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Tue, 18 Oct 2016 12:08:11 +0200 Subject: [PATCH 44/48] trying --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 7f811a830..92423380d 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1673,7 +1673,8 @@ void UDPStandardImplementation::startDataCallback(){ //socket details void *context = zmq_ctx_new(); void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // create a publisher - int val = 4; + int val = -1; + /*int val = 4;*/ zmq_setsockopt(zmqsocket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket //val = 10; //zmq_setsockopt(zmqsocket,ZMQ_SNDHWM,&val,sizeof(val)); //set SEND HIGH WATER MARK (8-9ms slower) @@ -1753,7 +1754,7 @@ void UDPStandardImplementation::startDataCallback(){ } - /* + /*/**/ //send final header //update frame details #ifdef DEBUG @@ -1766,7 +1767,7 @@ void UDPStandardImplementation::startDataCallback(){ zmq_send(zmqsocket, buf,len, ZMQ_SNDMORE); //send final data zmq_send (zmqsocket, "end", 3, 0); - */ + /* */ pthread_mutex_lock(&statusMutex); dataCallbackThreadsMask^=(1< Date: Tue, 18 Oct 2016 12:19:20 +0200 Subject: [PATCH 45/48] switched --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 92423380d..85d14ab52 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1673,8 +1673,8 @@ void UDPStandardImplementation::startDataCallback(){ //socket details void *context = zmq_ctx_new(); void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // create a publisher - int val = -1; - /*int val = 4;*/ + /*int val = -1;*/ + int val = 4; zmq_setsockopt(zmqsocket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket //val = 10; //zmq_setsockopt(zmqsocket,ZMQ_SNDHWM,&val,sizeof(val)); //set SEND HIGH WATER MARK (8-9ms slower) @@ -1754,7 +1754,7 @@ void UDPStandardImplementation::startDataCallback(){ } - /*/**/ + /* //send final header //update frame details #ifdef DEBUG @@ -1767,7 +1767,7 @@ void UDPStandardImplementation::startDataCallback(){ zmq_send(zmqsocket, buf,len, ZMQ_SNDMORE); //send final data zmq_send (zmqsocket, "end", 3, 0); - /* */ + */ pthread_mutex_lock(&statusMutex); dataCallbackThreadsMask^=(1< Date: Tue, 18 Oct 2016 14:05:05 +0200 Subject: [PATCH 46/48] back --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 85d14ab52..f9e38ec03 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1673,8 +1673,7 @@ void UDPStandardImplementation::startDataCallback(){ //socket details void *context = zmq_ctx_new(); void *zmqsocket = zmq_socket(context, ZMQ_PUSH); // create a publisher - /*int val = -1;*/ - int val = 4; + int val = -1; zmq_setsockopt(zmqsocket, ZMQ_LINGER, &val,sizeof(val)); // wait for the unsent packets before closing socket //val = 10; //zmq_setsockopt(zmqsocket,ZMQ_SNDHWM,&val,sizeof(val)); //set SEND HIGH WATER MARK (8-9ms slower) @@ -1754,7 +1753,7 @@ void UDPStandardImplementation::startDataCallback(){ } - /* + //send final header //update frame details #ifdef DEBUG @@ -1767,7 +1766,7 @@ void UDPStandardImplementation::startDataCallback(){ zmq_send(zmqsocket, buf,len, ZMQ_SNDMORE); //send final data zmq_send (zmqsocket, "end", 3, 0); - */ + pthread_mutex_lock(&statusMutex); dataCallbackThreadsMask^=(1< Date: Tue, 18 Oct 2016 14:31:05 +0200 Subject: [PATCH 47/48] overwriting --- slsReceiverSoftware/src/UDPStandardImplementation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index f9e38ec03..7e8cf4a05 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -2618,7 +2618,7 @@ void UDPStandardImplementation::stopWriting(int ithread, char* wbuffer){ //all threads need to close file, reset mask and exit loop missingPacketinFile = (long long int)numberOfFrames*packetsPerFrame-totalWritingPacketCount[ithread]; - if(missingPacketinFile){ + if(fileWriteEnable && (cbAction > DO_NOTHING) && missingPacketinFile){ updateFileHeader(ithread); fseek(sfilefd[ithread],0,0); fwrite((void*)fileHeader[ithread], 1, FILE_HEADER_SIZE, sfilefd[ithread]); From 7eafce87f938506be41a0ab4da80775f98cf3548 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Thu, 20 Oct 2016 08:13:42 +0200 Subject: [PATCH 48/48] fixed the print file packet loss progress bug --- .../src/UDPStandardImplementation.cpp | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp index 7e8cf4a05..79069fb02 100644 --- a/slsReceiverSoftware/src/UDPStandardImplementation.cpp +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -1529,8 +1529,10 @@ int UDPStandardImplementation::createNewFile(int ithread){ FILE_LOG(logDEBUG) << __AT__ << " called"; int index = 0; - if(totalWritingPacketCount[ithread]) + if(totalWritingPacketCount[ithread]){ index = frameIndex[ithread]; + cout << "\nThread " << ithread << "\tFile:" << completeFileName <