diff --git a/slsReceiverSoftware/.gitignore b/slsReceiverSoftware/.gitignore new file mode 100644 index 000000000..31dd1b8d2 --- /dev/null +++ b/slsReceiverSoftware/.gitignore @@ -0,0 +1,7 @@ +*~ +*.o +build/* +GPATH +GRTAGS +GSYMS +GTAGS diff --git a/slsReceiverSoftware/CMakeLists.txt b/slsReceiverSoftware/CMakeLists.txt new file mode 100644 index 000000000..79c9b6c73 --- /dev/null +++ b/slsReceiverSoftware/CMakeLists.txt @@ -0,0 +1,56 @@ +set(SOURCES + src/MySocketTCP.cpp + src/UDPInterface.cpp + src/UDPBaseImplementation.cpp + src/UDPStandardImplementation.cpp + src/slsReceiverTCPIPInterface.cpp + src/slsReceiver.cpp + src/slsReceiverUsers.cpp + src/utilities.cpp +) + +add_definitions( + -DDACS_INT -DSLS_RECEIVER_UDP_FUNCTIONS +) + +include_directories( + include + ../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} +) +set_target_properties(slsReceiverStatic PROPERTIES + ARCHIVE_OUTPUT_NAME SlsReceiver + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +) + +add_library(slsReceiverShared SHARED + ${SOURCES} + ${HEADERS} +) +set_target_properties(slsReceiverShared PROPERTIES + LIBRARY_OUTPUT_NAME SlsReceiver + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +) + +add_executable(slsReceiver + src/main.cpp +) +set_target_properties(slsReceiver PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +) +target_link_libraries(slsReceiver + slsReceiverShared + pthread + zmq + rt +) diff --git a/slsReceiverSoftware/Makefile b/slsReceiverSoftware/Makefile new file mode 100644 index 000000000..d94ac77a0 --- /dev/null +++ b/slsReceiverSoftware/Makefile @@ -0,0 +1,118 @@ +include ../Makefile.include + +DESTDIR ?= ../bin +LIBDIR ?= $(DESTDIR) +DOCDIR ?= docs +SRCDIR = src +TESTDIR = test +BUILDDIR = build +PROGS = $(DESTDIR)/slsReceiver + + +CFLAGS= -g -DC_ONLY -fPIC +#FLAGS+= #-DVERBOSE -DVERYVERBOSE + +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 + +ifeq ($(REST), yes) + SRC_CLNT += UDPRESTImplementation.cpp +endif + +MAIN_SRC = main.cpp + +DUMMY_MAIN_SRC = dummyMain.cpp + +OBJS=$(SRC_CLNT:%.cpp=$(BUILDDIR)/%.o) + + +$(info ) +$(info #######################################) +$(info # Compiling slsReceiverSoftware #) +$(info #######################################) +$(info ) + + +.PHONY: all intdoc package eigerReceiver clean + +all: builddir lib receiver + +dummy: $(DESTDIR)/dummyReceiver + +intdoc: $(SRC_H) $(SRC_CLNT) + doxygen doxy.config + +$(BUILDDIR)/%.o : $(SRCDIR)/%.cpp Makefile +ifeq ($(ROOTSLS),yes) + $(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) $(LIBZMQ) -lrt +endif + +lib: $(OBJS) $(DESTDIR)/libSlsReceiver.so $(DESTDIR)/libSlsReceiver.a + +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 $(LIBZMQ) -lrt + $(shell test -d $(DESTDIR) || mkdir -p $(DESTDIR)) + mv libSlsReceiver.so $(DESTDIR) + +$(DESTDIR)/libSlsReceiver.a: $(OBJS) + ar rcs libSlsReceiver.a $(OBJS) + mv libSlsReceiver.a $(DESTDIR) + + +$(DESTDIR)/slsReceiver: lib + $(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 $(LIBZMQ) -lrt +#$(EIGERFLAGS) + + +# Stand-alone Mysocket tests +mysocket_test: + g++ -o $(TESTDIR)/MySocketTCP.o -c $(SRCDIR)/MySocketTCP.cpp -I include + g++ -o $(TESTDIR)/rec $(TESTDIR)/MySocketTCP.o $(TESTDIR)/rec.cxx -I include + g++ -o $(TESTDIR)/send $(TESTDIR)/MySocketTCP.o $(TESTDIR)/send.cxx -I include + + +clean: buildclean + make testclean + if test -e $(DESTDIR)/libSlsReceiver.a; then rm $(DESTDIR)/libSlsReceiver.a;fi + if test -e $(DESTDIR)/libSlsReceiver.so; then rm $(DESTDIR)/libSlsReceiver.so;fi + if test -e $(PROGS); then rm $(PROGS);fi + +builddir: + if [ ! -d $(BUILDDIR) ]; then mkdir $(BUILDDIR); fi + +buildclean: + rm -rf $(OBJS) + +testclean: + if [ -f $(TESTDIR)/rec ]; then \ + cd $(TESTDIR) && rm *.o rec send; \ + fi + + +#------------------------------------------------------------------------------- + +install: package + +install_inc: + $(shell test -d $(DESTDIR) || mkdir -p $(DESTDIR)) + cp -P slsReceiver/slsReceiverUsers.h $(DESTDIR) + + diff --git a/slsReceiverSoftware/gitInfo.txt b/slsReceiverSoftware/gitInfo.txt new file mode 100644 index 000000000..6282454d0 --- /dev/null +++ b/slsReceiverSoftware/gitInfo.txt @@ -0,0 +1,9 @@ +Path: slsDetectorsPackage/slsReceiverSoftware +URL: origin git@git.psi.ch:sls_detectors_software/sls_receiver_software.git +Repository Root: origin git@git.psi.ch:sls_detectors_software/sls_receiver_software.git +Repsitory UUID: 2f78f4e118bfba443f84a3f72b2839610a8858a3 +Revision: 493 +Branch: developer +Last Changed Author: Dhanya_Maliakal +Last Changed Rev: 493 +Last Changed Date: 2016-11-30 10:36:46 +0100 diff --git a/slsReceiverSoftware/include/MySocketTCP.h b/slsReceiverSoftware/include/MySocketTCP.h new file mode 100644 index 000000000..31f56d453 --- /dev/null +++ b/slsReceiverSoftware/include/MySocketTCP.h @@ -0,0 +1,79 @@ + +#ifndef MY_SOCKET_TCP_H +#define MY_SOCKET_TCP_H + + + + +/** + * + * @libdoc The MySocketTCP class provides a simple interface for creating and sending/receiving data over a TCP socket. + * + * @short This class provides a simple interface for creating and sending/receiving data over a TCP socket. + * @author Ian Johnson + * @version 1.0 + */ + + + +//version 1.0, base development, Ian 19/01/09 + +/* Modified by anna on 19.01.2009 */ +/* + canceled SetupParameters() and varaibles intialized in the constructors' headers; + defined SEND_REC_MAX_SIZE (for compatibilty with mythen (and possibly other) pure C servers (i would move it to the common header file) + + added #ifndef C_ONLY... to cutout class definition when including in pure C servers (can be removed if SEND_REC_MAX_SIZE is moved to the common header file) + + defined private variables char hostname[1000] and int portno to store connection informations; + + defined public functions int getHostname(char *name) and int getPortNumber() to retrieve connection informations + + added public function int getErrorStatus() returning 1 if socketDescriptor<0 + + remove exits in the constructors and replace them with socketDescriptor=-1 + + replaced the argument of send/receive data with void (to avoid too much casting or compiler errors/warnings) + + added a function which really does not close the socket between send/receive (senddataonly, receivedataonly) + +*/ + + +/* Modified by Anna on 31.10.2012 + +developed and + +*/ + + +#include "genericSocket.h" +#define TCP_PACKET_SIZE 4096 + +class MySocketTCP: public genericSocket { + + public: + MySocketTCP(const char* const host_ip_or_name, unsigned short int const port_number): genericSocket(host_ip_or_name, port_number,TCP), last_keep_connection_open_action_was_a_send(0){setPacketSize(TCP_PACKET_SIZE);}; // sender (client): where to? ip + MySocketTCP(unsigned short int const port_number):genericSocket(port_number,TCP), last_keep_connection_open_action_was_a_send(0) {setPacketSize(TCP_PACKET_SIZE);}; // receiver (server) local no need for ip + + + //The following two functions will connectioned->send/receive->disconnect + int SendData(void* buf,int length);//length in characters + int ReceiveData(void* buf,int length); + + + //The following two functions stay connected, blocking other connections, and must be manually disconnected, + // when the last call is a SendData() or ReceiveData() the disconnection will be done automatically + //These function will also automatically disconnect->reconnect if + // two reads (or two writes) are called in a row to preserve the data send/receive structure + int SendDataAndKeepConnection(void* buf,int length); + int ReceiveDataAndKeepConnection(void* buf,int length); + + private: + + + bool last_keep_connection_open_action_was_a_send; + + +}; +#endif diff --git a/slsReceiverSoftware/include/RestHelper.h b/slsReceiverSoftware/include/RestHelper.h new file mode 100644 index 000000000..d789448ab --- /dev/null +++ b/slsReceiverSoftware/include/RestHelper.h @@ -0,0 +1,307 @@ +/** + * @file RestHelper.h + * @author Leonardo Sala + * @date Tue Mar 25 09:28:19 2014 + * + * @brief + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "JsonBox/Value.h" +#include "JsonBox/JsonParsingError.h" + +//#include "logger.h" + +#include +#include +#include +#include +#include + + + +using namespace Poco::Net; +using namespace Poco; +using namespace std; + +class RestHelper { + public: + + RestHelper(int timeout=10, int n_tries=1){ + /** + * + * + * @param timeout default=10 + * @param n_tries default=1 + */ + + http_timeout = timeout; + n_connection_tries = n_tries; + } + + ~RestHelper(){ + delete session; + }; + + + void set_connection_params(int timeout, int n_tries){ + http_timeout = timeout; + n_connection_tries = n_tries; + } + + + void get_connection_params(int *timeout, int *n_tries){ + *timeout = http_timeout; + *n_tries = n_connection_tries; + + } + + + void init(string hostname, int port){ + /** Initialize the RestHelper. Hostname and port parameters are not supposed to change. + * + * + * @param hostname FQDN of the host to connect to , e.g. www.iamfake.org, or sodoi.org + * @param port + * + * @return + */ + + //Check for http:// string + string proto_str = "http://"; + + if( size_t found = hostname.find(proto_str) != string::npos ){ + char c1[hostname.size()-found-1]; + size_t length1 = hostname.copy(c1, hostname.size()-found-1, proto_str.size()); + c1[length1]='\0'; + hostname = c1; + } + + full_hostname = "http://"+hostname; + session = new HTTPClientSession(hostname, port ); + session->setKeepAliveTimeout( Timespan( http_timeout,0) ); + }; + + + void init(string hostname_port){ + /** Initialize the RestHelper. Hostname_port parameters are not supposed to change. + * + * + * @param hostname FQDN and port of the host to connect to , e.g. www.iamfake.org:8080, or sodoi.org:1111. Default port is 8080 + * + * @return + */ + + //Check for http:// string + string proto_str = "http://"; + if( size_t found = hostname_port.find(proto_str) != string::npos ){ + char c1[hostname_port.size()-found-1]; + size_t length1 = hostname_port.copy(c1, hostname_port.size()-found-1, proto_str.size()); + c1[length1]='\0'; + hostname_port = c1; + } + + size_t found = hostname_port.rfind(":"); + char c1[ found ], c2[hostname_port.size()-found-1]; + string hostname; + size_t length1 = hostname_port.copy(c1, found); + + c1[length1]='\0'; + hostname = c1; + size_t length2 = hostname_port.copy(c2, found-1, found+1); + c2[length2]='\0'; + int port = atoi(c2); + + full_hostname = proto_str+hostname; + session = new HTTPClientSession(hostname,port ); + session->setKeepAliveTimeout( Timespan( http_timeout,0) ); + }; + + + int get_json(string request, string* answer){ + /** Retrieves a reply from the RESTful webservice. + * + * + * @param request Request without the hostname, e.g. if the full request would have been http://fake.org/fakemethod, request=fakemethod + * @param answer + * + * @return 0 if successful, -1 if failure happens. + */ + URI * uri = new URI(full_hostname+"/"+request); + string path(uri->getPathAndQuery()); + if (path.empty()) path = "/"; + + // send request + HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1); + req.setContentType("application/json\r\n"); + int code = send_request(session, req, answer); + delete uri; + return code; + }; + + + int get_json(string request, JsonBox::Value* json_value){ + /** + * + * + * @param request + * @param json_value + * + * @return + */ + URI *uri = new URI(full_hostname+"/"+request); + string path(uri->getPathAndQuery()); + if (path.empty()) path = "/"; + // send request + HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1); + req.setContentType("application/json\r\n"); + string answer; + int code = send_request(session, req, &answer); + if(code == 0 ) { + FILE_LOG(logDEBUG) << __AT__ << " REQUEST: " << " ANSWER: " << answer; + json_value->loadFromString(answer); + } + delete uri; + return code; + }; + + + int post_json(string request, string *answer, string request_body="{}"){ + /** + * + * + * @param request + * @param answer + * @param request_body Eventual arguments to the URL, e.g. action=login&name=mammamia + * + * @return + */ + //from: http://stackoverflow.com/questions/1499086/poco-c-net-ssl-how-to-post-https-request + URI *uri = new URI(full_hostname+"/"+request); + string path(uri->getPathAndQuery()); + if (path.empty()) path = "/"; + HTTPRequest req(HTTPRequest::HTTP_POST, path, HTTPMessage::HTTP_1_1 ); + req.setContentType("application/json\r\n"); + req.setContentLength( request_body.length() ); + int code = send_request(session, req, answer, request_body); + + delete uri; + return code; + } + + + int post_json(string request, JsonBox::Value* json_value, string request_body="{}"){ + /** + * + * + * @param request + * @param json_value + * @param request_body Eventual arguments to the URL, e.g. action=login&name=mammamia + * + * @return + */ + + URI *uri = new URI(full_hostname+"/"+request); + string path(uri->getPathAndQuery()); + if (path.empty()) path = "/"; + HTTPRequest req(HTTPRequest::HTTP_POST, path, HTTPMessage::HTTP_1_1 ); + //this does not work + //req.setContentType("application/json\r\n"); + //req.setContentLength( request.length() ); + string answer; + int code = send_request(session, req, &answer, request_body); + if(code==0){ + try{ + json_value->loadFromString(answer); + } + catch (JsonBox::JsonParsingError& e){ + try{ + json_value->loadFromString("{\"global_state\":\"" + answer + "\"}"); + } + catch(exception &e){ + FILE_LOG(logERROR) << "Exception converting answer: " << e.what() ; + } + } + } + delete uri; + return code; + } + + + private: + + HTTPClientSession *session; + string full_hostname; + /// HTTP timeout in seconds, default is 8 + int http_timeout; + /// Number of connection tries + int n_connection_tries; + + + int send_request(HTTPClientSession *session, HTTPRequest &req, string *answer, string request_body=""){ + /** + * + * + * @param session + * @param req + * @param answer + * @param request_body + * + * @return + */ + + int n = 0; + int code = -1; + while(n < n_connection_tries){ + + req.setContentType("application/json"); + //without this you need to tell the lenght: http://pocoproject.org/forum/viewtopic.php?f=12&t=5741&p=10019&hilit=post+json#p10019 + // request.setContentLength(my_string.length()); + req.setChunkedTransferEncoding(true); + try { + //istringstream rs(request_body); + //req.read(rs); + //cout << " --- " << rs << endl; + if (request_body == "") + session->sendRequest( (req) ); + else{ + ostream &os = session->sendRequest( req ) ; + os << request_body; + } + + HTTPResponse res; + istream &is = session->receiveResponse(res); + StreamCopier::copyToString(is, *answer); + code = res.getStatus(); + if (code != 200){ + FILE_LOG(logERROR) << "HTTP ERROR " << res.getStatus() << ": " << res.getReason() ; + code = -1; + } + else + code = 0; + return code; + } + catch (exception& e){ + FILE_LOG(logERROR) << "Exception connecting to "<< full_hostname << ": "<< e.what() << ", sleeping 5 seconds (" << n << "/"< + +/** + * @short does all the base functions for a receiver, set/get parameters, start/stop etc. + */ + +class UDPBaseImplementation : protected virtual slsReceiverDefs, public UDPInterface { + + public: + + /************************************************************************* + * Constructor & Destructor ********************************************** + * They access local cache of configuration or detector parameters ******* + *************************************************************************/ + /** + * Constructor + */ + UDPBaseImplementation(); + + /** + * Destructor + */ + virtual ~UDPBaseImplementation(); + + /* + * Initialize class members + */ + void initializeMembers(); + + /************************************************************************* + * Getters *************************************************************** + * They access local cache of configuration or detector parameters ******* + *************************************************************************/ + + //**initial parameters*** + /* + * Get detector hostname + * @return NULL or hostname or NULL if uninitialized (max of 1000 characters) + */ + char *getDetectorHostname() const; + + /* + * Get flipped data across 'axis' + * @return if data is flipped across 'axis' + */ + int getFlippedData(int axis=0) const; + + + //***file parameters*** + /** + * Get File Name Prefix (without frame index, file index and extension (_d0_f000000000000_8.raw)) + * @return NULL or file name prefix (max of 1000 characters) + */ + char *getFileName() const; + + /** + * Get File Path + * @return NULL or file path (max of 1000 characters) + */ + char *getFilePath() const; + + /** + * Get File Index + * @return file index of acquisition + */ + uint64_t getFileIndex() const; + + /** + * Get Scan Tag + * @return scan tag //FIXME: needed? (unsigned integer?) + */ + int getScanTag() const; + + /** + * Get if Frame Index is enabled (acquisition of more than 1 frame adds '_f000000000000' to file name ) + * @return true if frame index needed, else false + */ + bool getFrameIndexEnable() const; + + /** + * Get File Write Enable + * @return true if file write enabled, else false + */ + bool getFileWriteEnable() const; + + /** + * Get File Over Write Enable + * @return true if file over write enabled, else false + */ + bool getOverwriteEnable() const; + + /** + * Get data compression, by saving only hits (so far implemented only for Moench and Gotthard) + * @return true if data compression enabled, else false + */ + bool getDataCompressionEnable() const; + + + //***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; + + /** + * Get Current Frame Index for an entire acquisition (including all scans) + * @return current frame index (represents all scans too) + */ + int64_t getAcquisitionIndex() const; + + + //***connection parameters*** + /** + * Get UDP Port Number + * @return udp port number + */ + uint32_t getUDPPortNumber() const; + + /** + * Get Second UDP Port Number (eiger specific) + * @return second udp port number + */ + uint32_t getUDPPortNumber2() const; + + /** + * Get Ehernet Interface + * @ethernet interface. eg. eth0 or "" if listening to all (max of 1000 characters) + */ + char *getEthernetInterface() const; + + + //***acquisition parameters*** + /** + * Get Short Frame Enabled, later will be moved to getROI (so far only for gotthard) + * @return index of adc enabled, else -1 if all enabled + */ + int getShortFrameEnable() const; + + /** + * Get the Frequency of Frames Sent to GUI + * @return 0 for random frame requests, n for nth frame frequency + */ + uint32_t getFrameToGuiFrequency() const; + + /** + * Gets the timer between frames streamed when frequency is set to 0 + * @return timer between frames streamed + */ + uint32_t getFrameToGuiTimer() const; + + /** + * Get the data stream enable + * @return 1 to send via zmq, else 0 + */ + uint32_t getDataStreamEnable() const; + + + /** + * Get Acquisition Period + * @return acquisition period + */ + uint64_t getAcquisitionPeriod() const; + + /** + * Get Acquisition Time + * @return acquisition time + */ + uint64_t getAcquisitionTime() const; + + /* + * Get Number of Frames expected by receiver from detector + * The data receiver status will change from running to idle when it gets this number of frames FIXME: (Not implemented) + * @return number of frames expected + */ + uint64_t getNumberOfFrames() const; + + /** + * Get Dynamic Range or Number of Bits Per Pixel + * @return dynamic range that is 4, 8, 16 or 32 + */ + uint32_t getDynamicRange() const; + + /** + * Get Ten Giga Enable + * @return true if 10Giga enabled, else false (1G enabled) + */ + bool getTenGigaEnable() const; + + /** + * Get Fifo Depth + * @return fifo depth + */ + uint32_t getFifoDepth() const; + + + //***receiver status*** + /** + * Get Listening Status of Receiver + * @return can be idle, listening or error depending on if the receiver is listening or not + */ + 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; + + + + /************************************************************************* + * Setters *************************************************************** + * They modify the local cache of configuration or detector parameters *** + *************************************************************************/ + + //**initial parameters*** + /** + * Configure command line parameters + * @param config_map mapping of config parameters passed from command line arguments + */ + void configure(map config_map); + + /* + * Get flipped data across 'axis' + * @return if data is flipped across 'axis' + */ + void setFlippedData(int axis=0, int enable=-1); + + + //***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[]); + + /** + * Set File Path + * Checks for file directory existence before setting file path, + * If it doesn't exist, it will set it blank + * @param c file path (max of 1000 characters) + */ + void setFilePath(const char c[]); + + /** + * Set File Index of acquisition + * @param i file index of acquisition + */ + void setFileIndex(const uint64_t i); + + /** + * Set Scan Tag + * @param i scan tag //FIXME: needed? (unsigned integer?) + */ + void setScanTag(const int i); + + /** + * Set Frame Index Enable (acquisition of more than 1 frame adds '_f000000000000' to file name ) + * @param b true for frame index enable, else false + */ + void setFrameIndexEnable(const bool b); + + /** + * Set File Write Enable + * @param b true for file write enable, else false + */ + void setFileWriteEnable(const bool b); + + /** + * Set File Overwrite Enable + * @param b true for file overwrite enable, else false + */ + void setOverwriteEnable(const bool b); + + /** + * Set data compression, by saving only hits (so far implemented only for Moench and Gotthard) + * @param b true for data compression enable, else false + * @return OK or FAIL + */ + int setDataCompressionEnable(const bool b); + + + //***connection parameters*** + /** + * Set UDP Port Number + * @param i udp port number + */ + void setUDPPortNumber(const uint32_t i); + + /** + * Set Second UDP Port Number (eiger specific) + * @return second udp port number + */ + void setUDPPortNumber2(const uint32_t i); + + /** + * Set Ethernet Interface to listen to + * @param c ethernet inerface eg. eth0 (max of 1000 characters) + */ + void setEthernetInterface(const char* c); + + + //***acquisition parameters*** + /** + * Set Short Frame Enabled, later will be moved to getROI (so far only for gotthard) + * @param i index of adc enabled, else -1 if all enabled + */ + void setShortFrameEnable(const int i); + + /** + * Set the Frequency of Frames Sent to GUI + * @param freq 0 for random frame requests, n for nth frame frequency + * @return OK or FAIL + */ + int setFrameToGuiFrequency(const uint32_t freq); + + /** + * Sets the timer between frames streamed when frequency is set to 0 + * @param time_in_ms timer between frames streamed + */ + void setFrameToGuiTimer(const uint32_t time_in_ms); + + /** + * 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 + * @param i acquisition period + * @return OK or FAIL + */ + int setAcquisitionPeriod(const uint64_t i); + + /** + * Set Acquisition Time + * @param i acquisition time + * @return OK or FAIL + */ + int setAcquisitionTime(const uint64_t i); + + /** + * Set Number of Frames expected by receiver from detector + * The data receiver status will change from running to idle when it gets this number of frames + * @param i number of frames expected + * @return OK or FAIL + */ + int setNumberOfFrames(const uint64_t i); + + /** + * Set Dynamic Range or Number of Bits Per Pixel + * @param i dynamic range that is 4, 8, 16 or 32 + * @return OK or FAIL + */ + int setDynamicRange(const uint32_t i); + + /** + * Set Ten Giga Enable + * @param b true if 10Giga enabled, else false (1G enabled) + * @return OK or FAIL + */ + int setTenGigaEnable(const bool b); + + /** + * Set Fifo Depth + * @param i fifo depth value + * @return OK or FAIL + */ + int setFifoDepth(const uint32_t i); + + + /************************************************************************* + * Behavioral functions*************************************************** + * They may modify the status of the receiver **************************** + *************************************************************************/ + + + //***initial functions*** + /** + * Set receiver type (and corresponding detector variables in derived STANDARD class) + * It is the first function called by the client when connecting to receiver + * @param d detector type + * @return OK or FAIL + */ + int setDetectorType(const detectorType d); + + /** + * Sets detector hostname (and corresponding detector variables in derived REST class) + * It is second function called by the client when connecting to receiver. + * you can call this function only once. //FIXME: is this still valid, this implemented in derived REST class? + * @param c detector hostname + */ + void initialize(const char *c); + + + //***acquisition functions*** + /** + * Reset acquisition parameters such as total frames caught for an entire acquisition (including all scans) + */ + void resetAcquisitionCount(); + + /** + * Start Listening for Packets by activating all configuration settings to receiver + * @param c error message if FAIL + * @return OK or FAIL + */ + int startReceiver(char *c=NULL); + + /** + * Stop Listening for Packets + * Calls startReadout(), which stops listening and sets status to Transmitting + * When it has read every frame in buffer,it returns with the status Run_Finished + */ + void stopReceiver(); + + /** + * Stop Listening to Packets + * and sets status to Transmitting + */ + void startReadout(); + + /** + * Shuts down and deletes UDP Sockets + * @return OK or FAIL + */ + int shutDownUDPSockets(); + + /** + * 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(int ithread, char* c,char** raw, int64_t &startAcq, int64_t &startFrame); + + /** + * abort acquisition with minimum damage: close open files, cleanup. + * does nothing if state already is 'idle' + */ + void abort(); //FIXME: needed, isn't stopReceiver enough? + + /** + * Closes file / all files(if multiple files) + * @param ithread writer thread index + */ + 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*** + /** + * Call back for start acquisition + * callback arguments are + * filepath + * filename + * fileindex + * datasize + * + * return value is the action which decides what the user and default responsibilities to save data are + * 0 callback takes care of open,close,wrie file + * 1 callback writes file, we have to open, close it + * 2 we open, close, write file, callback does not do anything + */ + void registerCallBackStartAcquisition(int (*func)(char*, char*,int, int, void*),void *arg); + + /** + * Call back for acquisition finished + * callback argument is + * total frames caught + */ + void registerCallBackAcquisitionFinished(void (*func)(int, void*),void *arg); + + /** + * Call back for raw data + * args to raw data ready callback are + * framenum + * datapointer + * datasize in bytes + * file descriptor + * guidatapointer (NULL, no data required) + */ + void registerCallBackRawDataReady(void (*func)(int, char*, int, FILE*, char*, void*),void *arg); + + + + + protected: + + /************************************************************************* + * Class Members ********************************************************* + *************************************************************************/ + //**detector parameters*** + /** detector type */ + detectorType myDetectorType; + /** detector hostname */ + char detHostname[MAX_STR_LENGTH]; + /** Number of Packets per Frame*/ + uint32_t packetsPerFrame; + /** Acquisition Period */ + uint64_t acquisitionPeriod; + /** Acquisition Time */ + uint64_t acquisitionTime; + /** Frame Number */ + uint64_t numberOfFrames; + /** Dynamic Range */ + uint32_t dynamicRange; + /** Ten Giga Enable*/ + bool tengigaEnable; + /** Fifo Depth */ + uint32_t fifoDepth; + /** enable for flipping data across both axes */ + int flippedData[2]; + + //***receiver parameters*** + /** Maximum Number of Listening Threads/ UDP Ports */ + const static int MAX_NUMBER_OF_LISTENING_THREADS = 2; + /** Receiver Status */ + runStatus status; + /** Activated/Deactivated */ + int activated; + + //***connection parameters*** + /** Ethernet Interface */ + char eth[MAX_STR_LENGTH]; + /** Server UDP Port Number*/ + uint32_t udpPortNum[MAX_NUMBER_OF_LISTENING_THREADS]; + + //***file parameters*** + /** File Name without frame index, file index and extension (_d0_f000000000000_8.raw)*/ + char fileName[MAX_STR_LENGTH]; + /** File Path */ + char filePath[MAX_STR_LENGTH]; + /** File Index */ + uint64_t fileIndex; + /** Scan Tag */ + int scanTag; + /** Frame Index Enable */ + bool frameIndexEnable; + /** File Write enable */ + bool fileWriteEnable; + /** Overwrite enable */ + bool overwriteEnable; + /** Data Compression Enable - save only hits */ + bool dataCompressionEnable; + + //***acquisition count parameters*** + /** Total packets caught for an entire acquisition (including all scans) */ + uint64_t totalPacketsCaught; + /** Packets Caught for each real time acquisition (eg. for each scan) */ + uint64_t packetsCaught; + + //***acquisition indices parameters*** + /** Actual current frame index of an entire acquisition (including all scans) */ + uint64_t acquisitionIndex; + + //***acquisition parameters*** + /* 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; + /** Timer of Frames sent to GUI when frequency is 0 */ + uint32_t frameToGuiTimerinMS; + /** Data Stream Enable from Receiver */ + int32_t dataStreamEnable; + static const int DEFAULT_STREAMING_TIMER = 500; + + + + + //***callback parameters*** + /** + * function being called back for start acquisition + * callback arguments are + * filepath + * filename + * fileindex + * datasize + * + * return value is + * 0 callback takes care of open,close,wrie file + * 1 callback writes file, we have to open, close it + * 2 we open, close, write file, callback does not do anything + */ + int (*startAcquisitionCallBack)(char*, char*,int, int, void*); + void *pStartAcquisition; + + /** + * function being called back for acquisition finished + * callback argument is + * total frames caught + */ + void (*acquisitionFinishedCallBack)(int, void*); + void *pAcquisitionFinished; + + + /** + * function being called back for raw data + * args to raw data ready callback are + * framenum + * datapointer + * datasize in bytes + * file descriptor + * guidatapointer (NULL, no data required) + */ + void (*rawDataReadyCallBack)(int, char*, int, FILE*, char*, void*); + void *pRawDataReady; + + +private: + +}; + + +#endif + +//#endif diff --git a/slsReceiverSoftware/include/UDPInterface.h b/slsReceiverSoftware/include/UDPInterface.h new file mode 100644 index 000000000..8bdc1a87e --- /dev/null +++ b/slsReceiverSoftware/include/UDPInterface.h @@ -0,0 +1,577 @@ +#ifndef UDPINTERFACE_H +#define UDPINTERFACE_H + +/*********************************************** + * @file UDPInterface.h + * @short Base class with all the functions for the UDP inteface of the receiver + ***********************************************/ +/** + * \mainpage Base class with all the functions for the UDP inteface of the receiver + */ + +/** + * @short Base class with all the functions for the UDP inteface of the receiver + */ + +#include + +#include "sls_receiver_defs.h" +#include "receiver_defs.h" +#include "utilities.h" +#include "logger.h" + + +class UDPInterface { + + + /* abstract class that defines the UDP interface of an sls detector data receiver. + * + * Use the factory method UDPInterface::create() to get an instance: + * + * UDPInterface *udp_interface = UDPInterface::create() + * + * + * supported sequence of method-calls: + * + * initialize() : once and only once after create() //FIXME: only once functionality implemented in the derived REST class, so not mention here? + * + * get*() : anytime after initialize(), multiples times + * + * set*() : anytime after initialize(), multiple times + * + * startReceiver(): anytime after initialize(). Will fail in TCPIP itself if state already is 'running': + * + * Only startReceiver() does change the data receiver configuration, it does pass the whole configuration cache to the data receiver. + * + * abort(), //FIXME: needed? + * + * stopReceiver() : anytime after initialize(). Will do nothing if state already is idle. + * Otherwise, sets status to transmitting when shutting down sockets + * then to run_finished when all data obtained + * then to idle when returning from this function + * + * + * getStatus() returns the actual state of the data receiver - idle, running or error, enum defined in include/sls_receiver_defs.h + * + * + * + * get*() and set*() methods access the local cache of configuration values only and *do not* modify the data receiver settings. + * + * set methods return nothing, use get methods to validate a set method success + * + * get-methods that return a char array (char *) allocate a new array at each call. The caller is responsible to free the allocated space: + * + * char *c = receiver->getFileName(); + * Or + * FIXME: so that the pointers are not shared external to the class, do the following way in the calling method? + * char *c = new char[MAX_STR_LENGTH]; + * strcpy(c,receiver->getFileName()); + * .... + * + * delete[] c; + * + * All pointers passed in externally will be allocated and freed by the calling function + * + * OK and FAIL are defined in include/sls_receiver_defs.h for functions implementing behavior + * + */ + + public: + + /************************************************************************* + * Constructor & Destructor ********************************************** + * They access local cache of configuration or detector parameters ******* + *************************************************************************/ + /** + * Constructor + * Only non virtual function implemented in this class + * Factory create method to create a standard or REST object + * @param [in] receiver_type type can be standard or REST + * @return a UDPInterface reference to object depending on receiver type + */ + static UDPInterface *create(string receiver_type = "standard"); + + /** + * Destructor + */ + virtual ~UDPInterface() {}; + + + + /************************************************************************* + * Getters *************************************************************** + * They access local cache of configuration or detector parameters ******* + *************************************************************************/ + + //**initial/detector parameters*** + /* + * Get detector hostname + * @return hostname or NULL if uninitialized, must be released by calling function (max of 1000 characters) + */ + virtual char *getDetectorHostname() const = 0; + + /* + * Get flipped data across 'axis' + * @return if data is flipped across 'axis' + */ + virtual int getFlippedData(int axis=0) const = 0; + + + //***file parameters*** + /** + * Get File Name Prefix (without frame index, file index and extension (_d0_f000000000000_8.raw)) + * @return NULL or pointer to file name prefix, must be released by calling function (max of 1000 characters) + */ + virtual char *getFileName() const = 0; + + /** + * Get File Path + * @return NULL or pointer to file path, must be released by calling function (max of 1000 characters) + */ + virtual char *getFilePath() const = 0; + + /** + * Get File Index + * @return NULL or file index of acquisition + */ + virtual uint64_t getFileIndex() const = 0; + + /** + * Get Scan Tag + * @return scan tag //FIXME: needed? (unsigned integer?) + */ + virtual int getScanTag() const = 0; + + /** + * Get if Frame Index is enabled (acquisition of more than 1 frame adds '_f000000000000' to file name ) + * @return true if frame index needed, else false + */ + virtual bool getFrameIndexEnable() const = 0; + + /** + * Get File Write Enable + * @return true if file write enabled, else false + */ + virtual bool getFileWriteEnable() const = 0; + + /** + * Get File Over Write Enable + * @return true if file over write enabled, else false + */ + virtual bool getOverwriteEnable() const = 0; + + /** + * Get data compression, by saving only hits (so far implemented only for Moench and Gotthard) + * @return true if data compression enabled, else false + */ + virtual bool getDataCompressionEnable() const = 0; + + + //***acquisition count parameters*** + /** + * Get Total Frames Caught for an entire acquisition (including all scans) + * @return total number of frames caught for entire acquisition + */ + virtual uint64_t getTotalFramesCaught() const = 0; + + /** + * Get Frames Caught for each real time acquisition (eg. for each scan) + * @return number of frames caught for each scan + */ + virtual uint64_t getFramesCaught() const = 0; + + /** + * Get Current Frame Index for an entire acquisition (including all scans) + * @return current frame index (represents all scans too) or -1 if no packets caught + */ + virtual int64_t getAcquisitionIndex() const = 0; + + + //***connection parameters*** + /** + * Get UDP Port Number + * @return udp port number + */ + virtual uint32_t getUDPPortNumber() const = 0; + + /** + * Get Second UDP Port Number (eiger specific) + * @return second udp port number + */ + virtual uint32_t getUDPPortNumber2() const = 0; + + /** + * Get Ehernet Interface + * @return ethernet interface. eg. eth0 (max of 1000 characters) + */ + virtual char *getEthernetInterface() const = 0; + + + //***acquisition parameters*** + /** + * Get Short Frame Enabled, later will be moved to getROI (so far only for gotthard) + * @return index of adc enabled, else -1 if all enabled + */ + virtual int getShortFrameEnable() const = 0; + + /** + * Get the Frequency of Frames Sent to GUI + * @return 0 for random frame requests, n for nth frame frequency + */ + virtual uint32_t getFrameToGuiFrequency() const = 0; + + /** + * Gets the timer between frames streamed when frequency is set to 0 + * @return timer between frames streamed + */ + virtual uint32_t getFrameToGuiTimer() 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 + */ + virtual uint64_t getAcquisitionPeriod() const = 0; + + /** + * Get Acquisition Time + * @return acquisition time + */ + virtual uint64_t getAcquisitionTime() const = 0; + + /* + * Get Number of Frames expected by receiver from detector + * The data receiver status will change from running to idle when it gets this number of frames FIXME: (Not implemented) + * @return number of frames expected + */ + virtual uint64_t getNumberOfFrames() const = 0; + + /** + * Get Dynamic Range or Number of Bits Per Pixel + * @return dynamic range that is 4, 8, 16 or 32 + */ + virtual uint32_t getDynamicRange() const = 0; + + /** + * Get Ten Giga Enable + * @return true if 10Giga enabled, else false (1G enabled) + */ + virtual bool getTenGigaEnable() const = 0; + + /** + * Get Fifo Depth + * @return fifo depth + */ + virtual uint32_t getFifoDepth() const = 0; + + //***receiver status*** + /** + * Get Listening Status of Receiver + * @return can be idle, listening or error depending on if the receiver is listening or not + */ + 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; + + + /************************************************************************* + * Setters *************************************************************** + * They modify the local cache of configuration or detector parameters *** + *************************************************************************/ + + //**initial parameters*** + /** + * Configure command line parameters + * @param config_map mapping of config parameters passed from command line arguments + */ + virtual void configure(map config_map) = 0; + + /* + * Get flipped data across 'axis' + * @return if data is flipped across 'axis' + */ + virtual void setFlippedData(int axis=0, int enable=-1) = 0; + + + //***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) + */ + virtual void setFileName(const char c[]) = 0; + + /** + * Set File Path + * Checks for file directory existence before setting file path + * @param c file path (max of 1000 characters) + */ + virtual void setFilePath(const char c[]) = 0; + + /** + * Set File Index of acquisition + * @param i file index of acquisition + */ + virtual void setFileIndex(const uint64_t i) = 0; + + /** + * Set Scan Tag + * @param i scan tag //FIXME: needed? (unsigned integer?) + */ + virtual void setScanTag(const int i) = 0; + + /** + * Set Frame Index Enable (acquisition of more than 1 frame adds '_f000000000000' to file name ) + * @param b true for frame index enable, else false + */ + virtual void setFrameIndexEnable(const bool b) = 0; + + /** + * Set File Write Enable + * @param b true for file write enable, else false + */ + virtual void setFileWriteEnable(const bool b) = 0; + + /** + * Set File Overwrite Enable + * @param b true for file overwrite enable, else false + */ + virtual void setOverwriteEnable(const bool b) = 0; + + /** + * Set data compression, by saving only hits (so far implemented only for Moench and Gotthard) + * @param b true for data compression enable, else false + * @return OK or FAIL + */ + virtual int setDataCompressionEnable(const bool b) = 0; + + //***connection parameters*** + /** + * Set UDP Port Number + * @param i udp port number + */ + virtual void setUDPPortNumber(const uint32_t i) = 0; + + /** + * Set Second UDP Port Number (eiger specific) + * @return second udp port number + */ + virtual void setUDPPortNumber2(const uint32_t i) = 0; + + /** + * Set Ethernet Interface to listen to + * @param c ethernet inerface eg. eth0 (max of 1000 characters) + */ + virtual void setEthernetInterface(const char* c) = 0; + + + //***acquisition parameters*** + /** + * Set Short Frame Enabled, later will be moved to getROI (so far only for gotthard) + * @param i index of adc enabled, else -1 if all enabled + */ + virtual void setShortFrameEnable(const int i) = 0; + + /** + * Set the Frequency of Frames Sent to GUI + * @param freq 0 for random frame requests, n for nth frame frequency + * @return OK or FAIL + */ + virtual int setFrameToGuiFrequency(const uint32_t freq) = 0; + + /** + * Sets the timer between frames streamed when frequency is set to 0 + * @param time_in_ms timer between frames streamed + */ + virtual void setFrameToGuiTimer(const uint32_t time_in_ms) = 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 + * @param i acquisition period + * @return OK or FAIL + */ + virtual int setAcquisitionPeriod(const uint64_t i) = 0; + + /** + * Set Acquisition Time + * @param i acquisition time + * @return OK or FAIL + */ + virtual int setAcquisitionTime(const uint64_t i) = 0; + + /** + * Set Number of Frames expected by receiver from detector + * The data receiver status will change from running to idle when it gets this number of frames FIXME: (Not implemented) + * @param i number of frames expected + * @return OK or FAIL + */ + virtual int setNumberOfFrames(const uint64_t i) = 0; + + /** + * Set Dynamic Range or Number of Bits Per Pixel + * @param i dynamic range that is 4, 8, 16 or 32 + * @return OK or FAIL + */ + virtual int setDynamicRange(const uint32_t i) = 0; + + /** + * Set Ten Giga Enable + * @param b true if 10Giga enabled, else false (1G enabled) + * @return OK or FAIL + */ + virtual int setTenGigaEnable(const bool b) = 0; + + /** + * Set Fifo Depth + * @param i fifo depth value + * @return OK or FAIL + */ + virtual int setFifoDepth(const uint32_t i) = 0; + + + /************************************************************************* + * Behavioral functions*************************************************** + * They may modify the status of the receiver **************************** + *************************************************************************/ + + + //***initial functions*** + /** + * Set receiver type (and corresponding detector variables in derived STANDARD class) + * It is the first function called by the client when connecting to receiver + * @param d detector type + * @return OK or FAIL + */ + virtual int setDetectorType(const slsReceiverDefs::detectorType d) = 0; + + /** + * Sets detector hostname (and corresponding detector variables in derived REST class) + * It is second function called by the client when connecting to receiver. + * you can call this function only once. //FIXME: is this still valid, this implemented in derived REST class? + * @param c detector hostname + */ + virtual void initialize(const char *c) = 0; + + + //***acquisition functions*** + /** + * Reset acquisition parameters such as total frames caught for an entire acquisition (including all scans) + */ + virtual void resetAcquisitionCount() = 0; + + /** + * Start Listening for Packets by activating all configuration settings to receiver + * @param c error message if FAIL + * @return OK or FAIL + */ + virtual int startReceiver(char *c=NULL) = 0; + + /** + * Stop Listening for Packets + * Calls startReadout(), which stops listening and sets status to Transmitting + * When it has read every frame in buffer,it returns with the status Run_Finished + */ + virtual void stopReceiver() = 0; + + /** + * Stop Listening to Packets + * and sets status to Transmitting + */ + virtual void startReadout() = 0; + + /** + * Shuts down and deletes UDP Sockets + * @return OK or FAIL + */ + virtual int shutDownUDPSockets() = 0; + + /** + * 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(int ithread, char* c,char** raw, int64_t &startAcq, int64_t &startFrame)=0; + + /** + * abort acquisition with minimum damage: close open files, cleanup. + * does nothing if state already is 'idle' + */ + virtual void abort() = 0; //FIXME: needed, isnt stopReceiver enough? + + /** + * Closes file / all files(if multiple files) + * @param ithread writer thread index + */ + 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 + * callback arguments are + * filepath + * filename + * fileindex + * datasize + * + * return value is + * 0 callback takes care of open,close,wrie file + * 1 callback writes file, we have to open, close it + * 2 we open, close, write file, callback does not do anything + */ + virtual void registerCallBackStartAcquisition(int (*func)(char*, char*,int, int, void*),void *arg) = 0; + + /** + * Call back for acquisition finished + * callback argument is + * total frames caught + */ + virtual void registerCallBackAcquisitionFinished(void (*func)(int, void*),void *arg) = 0; + + /** + * Call back for raw data + * args to raw data ready callback are + * framenum + * datapointer + * datasize in bytes + * file descriptor + * guidatapointer (NULL, no data required) + */ + virtual void registerCallBackRawDataReady(void (*func)(int, char*, int, FILE*, char*, void*),void *arg) = 0; + + + protected: + private: + +}; + +#endif /* #ifndef UDPINTERFACE_H */ diff --git a/slsReceiverSoftware/include/UDPRESTImplementation.h b/slsReceiverSoftware/include/UDPRESTImplementation.h new file mode 100644 index 000000000..14296f648 --- /dev/null +++ b/slsReceiverSoftware/include/UDPRESTImplementation.h @@ -0,0 +1,156 @@ +//#ifdef REST +#ifndef UDP_REST_IMPLEMENTATION_H +#define UDP_REST_IMPLEMENTATION_H +/********************************************//** + * @file UDPRESTImplementation.h + * @short does all the functions for a receiver, set/get parameters, start/stop etc. + ***********************************************/ + + +#include "UDPBaseImplementation.h" +#include "RestHelper.h" + +#include +#include + +/** + * @short does all the functions for a receiver, set/get parameters, start/stop etc. + */ + +class UDPRESTImplementation : protected virtual slsReceiverDefs, public UDPBaseImplementation { +public: + /************************************************************************* + * Constructor & Destructor ********************************************** + *************************************************************************/ + /** + * Constructor + */ + UDPRESTImplementation(); + + /** + * Destructor + */ + virtual ~UDPRESTImplementation(); + + + protected: + + /************************************************************************* + * Getters *************************************************************** + * They access local cache of configuration or detector parameters ******* + *************************************************************************/ + /** + * Get Rest State + */ + string get_rest_state(RestHelper * rest/*, string *rest_state*/); + + + /************************************************************************* + * Setters *************************************************************** + * They modify the local cache of configuration or detector parameters *** + *************************************************************************/ + /** + * Initialize REST + */ + void initialize_REST(); + + + + + public: + /************************************************************************* + * Getters *************************************************************** + * They access local cache of configuration or detector parameters ******* + *************************************************************************/ + + + /************************************************************************* + * Setters *************************************************************** + * They modify the local cache of configuration or detector parameters *** + *************************************************************************/ + + /** + * Overridden method + * Configure command line parameters + * @param config_map mapping of config parameters passed from command line arguments + */ + void configure(map config_map); + + + /************************************************************************* + * Behavioral functions*************************************************** + * They may modify the status of the receiver **************************** + *************************************************************************/ + + /** + * Overridden method + * Start Listening for Packets by activating all configuration settings to receiver + * When this function returns, it has status RUNNING(upon SUCCESS) or IDLE (upon failure) + * @param c error message if FAIL + * @return OK or FAIL + */ + int startReceiver(char *c=NULL); + + /** + * Overridden method + * Stop Listening for Packets + * Calls startReadout(), which stops listening and sets status to Transmitting + * When it has read every frame in buffer, the status changes to Run_Finished + * When this function returns, receiver has status IDLE + * Pre: status is running, semaphores have been instantiated, + * Post: udp sockets shut down, status is idle, semaphores destroyed + */ + void stopReceiver(); + + /** + * Overridden method + * Stop Listening to Packets + * and sets status to Transmitting + * Next step would be to get all data and stop receiver completely and return with idle state + * Pre: status is running, udp sockets have been initialized, stop receiver initiated + * Post:udp sockets closed, status is transmitting, + */ + void startReadout(); + + /** + * Overridden method + * Shuts down and deletes UDP Sockets + * TCPIPInterface can also call this in case of illegal shutdown of receiver + * @return OK or FAIL + */ + int shutDownUDPSockets(); + + /** + * Overridden method + * Get the buffer-current frame read by receiver + * @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); + + /** + * 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 + */ + void closeFile(int i = -1); + + uint64_t getTotalFramesCaught() const; + + +private: + bool isInitialized; + RestHelper * rest ; + int rest_port; // receiver backend port + string rest_hostname; // receiver hostname + bool is_main_receiver; + +}; + + +#endif + +//#endif /*REST*/ diff --git a/slsReceiverSoftware/include/UDPStandardImplementation.h b/slsReceiverSoftware/include/UDPStandardImplementation.h new file mode 100644 index 000000000..ad455e791 --- /dev/null +++ b/slsReceiverSoftware/include/UDPStandardImplementation.h @@ -0,0 +1,847 @@ +//#ifdef UDP_BASE_IMPLEMENTATION +#ifndef UDP_STANDARD_IMPLEMENTATION_H +#define UDP_STANDARD_IMPLEMENTATION_H +/********************************************//** + * @file UDPBaseImplementation.h + * @short does all the functions for a receiver, set/get parameters, start/stop etc. + ***********************************************/ + +#include "UDPBaseImplementation.h" + +#include "genericSocket.h" +#include "circularFifo.h" +#include "singlePhotonDetector.h" +#include "slsReceiverData.h" +#include "moenchCommonMode.h" + + +#ifdef MYROOT1 +#include +#include +#endif + +#include +#include +#include +#include + + +/** + * @short does all the functions for a receiver, set/get parameters, start/stop etc. + */ + + +class UDPStandardImplementation: private virtual slsReceiverDefs, public UDPBaseImplementation { + public: + + + /************************************************************************* + * Constructor & Destructor ********************************************** + *************************************************************************/ + /** + * Constructor + */ + UDPStandardImplementation(); + + /** + * Destructor + */ + virtual ~UDPStandardImplementation(); + + + /************************************************************************* + * Getters *************************************************************** + * They access local cache of configuration or detector parameters ******* + *************************************************************************/ + + + /************************************************************************* + * Setters *************************************************************** + * They modify the local cache of configuration or detector parameters *** + *************************************************************************/ + + //**initial parameters*** + //*** 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) + * @param b true for data compression enable, else false + * @return OK or FAIL + */ + 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 + * Set Short Frame Enabled, later will be moved to getROI (so far only for gotthard) + * @param i index of adc enabled, else -1 if all enabled + */ + void setShortFrameEnable(const int i); + + /** + * Set the Frequency of Frames Sent to GUI + * @param freq 0 for random frame requests, n for nth frame frequency + * @return OK or FAIL + */ + 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 + * Set Acquisition Period + * @param i acquisition period + * @return OK or FAIL + */ + int setAcquisitionPeriod(const uint64_t i); + + /** + * Set Acquisition Time + * @param i acquisition time + * @return OK or FAIL + */ + int setAcquisitionTime(const uint64_t i); + + /** + * Overridden method + * Set Number of Frames expected by receiver from detector + * The data receiver status will change from running to idle when it gets this number of frames + * @param i number of frames expected + * @return OK or FAIL + */ + int setNumberOfFrames(const uint64_t i); + + /** + * Overridden method + * Set Dynamic Range or Number of Bits Per Pixel + * @param i dynamic range that is 4, 8, 16 or 32 + * @return OK or FAIL + */ + int setDynamicRange(const uint32_t i); + + /** + * Overridden method + * Set Ten Giga Enable + * @param b true if 10Giga enabled, else false (1G enabled) + * @return OK or FAIL + */ + int setTenGigaEnable(const bool b); + + + /** + * Overridden method + * Set Fifo Depth + * @param i fifo depth value + * @return OK or FAIL + */ + int setFifoDepth(const uint32_t i); + + /************************************************************************* + * Behavioral functions*************************************************** + * They may modify the status of the receiver **************************** + *************************************************************************/ + + //***initial functions*** + /** + * Overridden method + * Set receiver type (and corresponding detector variables in derived STANDARD class) + * It is the first function called by the client when connecting to receiver + * @param d detector type + * @return OK or FAIL + */ + int setDetectorType(const detectorType d); + + //***acquisition functions*** + /** + * Overridden method + * Reset acquisition parameters such as total frames caught for an entire acquisition (including all scans) + */ + void resetAcquisitionCount(); + + /** + * Overridden method + * Start Listening for Packets by activating all configuration settings to receiver + * When this function returns, it has status RUNNING(upon SUCCESS) or IDLE (upon failure) + * @param c error message if FAIL + * @return OK or FAIL + */ + int startReceiver(char *c=NULL); + + /** + * Overridden method + * Stop Listening for Packets + * Calls startReadout(), which stops listening and sets status to Transmitting + * When it has read every frame in buffer, the status changes to Run_Finished + * When this function returns, receiver has status IDLE + * Pre: status is running, semaphores have been instantiated, + * Post: udp sockets shut down, status is idle, semaphores destroyed + */ + void stopReceiver(); + + /** + * Overridden method + * Stop Listening to Packets + * and sets status to Transmitting + * Next step would be to get all data and stop receiver completely and return with idle state + * Pre: status is running, udp sockets have been initialized, stop receiver initiated + * Post:udp sockets closed, status is transmitting + */ + void startReadout(); + + /** + * Overridden method + * Shuts down and deletes UDP Sockets + * TCPIPInterface can also call this in case of illegal shutdown of receiver + * @return OK or FAIL + */ + int shutDownUDPSockets(); + + /** + * 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(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) + * TCPIPInterface can also call this in case of illegal shutdown of receiver + * @param ithread writer thread index + */ + 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); + +private: + /************************************************************************* + * Getters *************************************************************** + * They access local cache of configuration or detector parameters ******* + *************************************************************************/ + +/* + uint64_t (*getFrameNumber)(); + uint64_t eigerGetFrameNumber(); + uint64_t generalGetFrameNumber(); + getframenumber = &generalgetframenumber; + if(dettpe == eiger) getframenumber = &eigerGerFramenumber; + + call using getframenumber(); +*/ + + //**initial parameters*** + + /** + * Delete and free member parameters + */ + void deleteMembers(); + + /** + * Deletes all the filter objects for single photon data + * Deals with data compression + */ + void deleteFilter(); + + /** + * Initialize base member parameters + */ + void initializeBaseMembers(); + + /** + * Initialize member parameters + */ + void initializeMembers(); + + /** + * Sets up all the filter objects for single photon data + * Deals with data compression + */ + void initializeFilter(); + + /** + * Set up the Fifo Structure for processing buffers + * between listening and writer threads + * When the parameters ahve been determined and if fifostructure needs to be changes, + * the listerning and writing threads are also destroyed together with this + * @return OK or FAIL + */ + int setupFifoStructure(); + + + + /************************************************************************* + * 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 + * @param destroy is true to destroy all the threads + */ + int createListeningThreads(bool destroy = false); + + /** + * Create Writer Threads + * @param destroy is true to destroy all the threads + * @return OK or FAIL + */ + int createWriterThreads(bool destroy = false); + + + + + /** + * Set Thread Priorities + */ + void setThreadPriorities(); + + /** + * Creates UDP Sockets + * @return OK or FAIL + */ + int createUDPSockets(); + + /** + * Initializes writer variables and creates the first file + * also does the startAcquisitionCallBack + * @return OK or FAIL + */ + int setupWriter(); + + /** + * Creates new file and reset some parameters + * @param ithread writer thread index + * @return OK or FAIL + */ + int createNewFile(int ithread); + + /** + * Creates new tree and file for compression + * @param ithread thread number + * @param iframe frame number + * @return OK or FAIL + */ + 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 + */ + static void* startListeningThread(void *this_pointer); + + /** + * Static function - Starts Writing Thread of this object + * @param this_pointer pointer to this object + */ + 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 + * This is continuously looped for each buffer in a nested loop, which is again looped for each acquisition + * Exits only for changing dynamic range, 10G parameters etc and recreated + * + */ + void startListening(); + + /** + * Called by startListening + * Listens to buffer, until packet(s) received or shutdownUDPsocket called by client + * 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 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 cSize, char* temp); + + /** + * Called by startListening + * Creates the packets + * @param ithread listening thread index + * @return the number of bytes actually received + */ + int prepareAndListenBufferDeactivated(int ithread); + + + /** + * Called by startListening + * Listens to each packet and copies only complete frames + * until all receiver or shutdownUDPsocket called by client + * @param ithread listening thread index + * @return the number of bytes copied to buffer + */ + int prepareAndListenBufferCompleteFrames(int ithread); + + /** + * Called by startListening + * Its called for the first packet of a scan or acquistion + * Sets the startframeindices and the variables to know if acquisition started + * @param ithread listening thread number + */ + void startFrameIndices(int ithread); + + /** + * Called by prepareAndListenBuffer + * This is called when udp socket is shut down by client + * It pushes ffff instead of packet number into fifo + * to inform writers about the end of listening session + * Then sets the listening mask so that it stops listening and wait for next acquisition trigger + * @param ithread listening thread number + * @param numbytes number of bytes received + */ + void stopListening(int ithread, int numbytes); + + /* + * Called by startListening for gotthard and moench to handle split frames + * It processes listening thread buffers by ensuring split frames are in the same buffer + * @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, int rc); + + /** + * Thread started which writes packets to file. + * It calls popAndCheckEndofAcquisition to pop fifo and check if it is a dummy end buffer + * It then calls a function to process and write packets to file and pushes the addresses into the fifoFree + * This is continuously looped for each buffer in a nested loop, which is again looped for each acquisition + * Exits only for changing dynamic range, 10G parameters etc and recreated + * + */ + void startWriting(); + + /** + * Called by processWritingBuffer and processWritingBufferPacketByPacket + * When dummy-end buffers are popped from all FIFOs (acquisition over), this is called + * It frees the FIFO addresses, closes all files + * For data compression, it waits for all threads to be done + * Changes the status to RUN_FINISHED and prints statistics + * @param ithread writing thread index + * @param wbuffer writing buffer popped out from FIFO + */ + void stopWriting(int ithread, char* wbuffer); + + /** + * Called by processWritingBuffer and processWritingBufferPacketByPacket + * Updates parameters, (writes headers for eiger) and writes to file when not a dummy frame + * Copies data for gui display and frees addresses popped from FIFOs + * @param ithread writing thread index + * @param wbuffer writing buffer popped out from FIFO + * @param npackets number of packets + */ + void handleWithoutDataCompression(int ithread, char* wbuffer,uint32_t npackets); + + /** + * Called by startWriting for jungfrau and eiger + * writes complete frames to file + * Copies data for gui display and frees addresses popped from FIFOs + * @param ithread writing thread index + * @param wbuffer writing buffer popped out from FIFO + */ + void handleCompleteFramesOnly(int ithread, char* wbuffer); + + /** + * 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(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); + + /** + * Updates the file header char aray, each time the corresp parameter is changed + * @param ithread writer thread index + */ + 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 + * @param numpackets number of packets to copy + */ + void copyFrameToGui(int ithread, char* buffer, uint32_t numpackets); + + void waitWritingBufferForNextAcquisition(int ithread); + + /** + * Called by processWritingBuffer + * Processing fifo popped buffers for data compression + * Updates parameters and writes to file + * Copies data for gui display and frees addresses popped from FIFOs + * @param ithread writing thread number + * @param wbuffer writer buffer + * @param nf number of frames + */ + void handleDataCompression(int ithread, char* wbuffer, uint64_t &nf); + + + /** + * Get Frame Number + * @param ithread writer thread index + * @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 + * @oaram bunchid reference to the bunch id + * @return OK or FAIL + */ + int getFrameandPacketNumber(int ithread, char* wbuffer, uint64_t &framenumber, uint32_t &packetnumber, uint32_t &subframenumber, uint64_t &bunchid); + + /** + * 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 ********************************************************* + *************************************************************************/ + + /** 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*** + /*Detector Readout ID*/ + int detID; + + /** Size of 1 buffer processed at a time */ + int bufferSize; + + /** One Packet Size including headers */ + int onePacketSize; + + /** One Packet Size without headers */ + int oneDataSize; + + /** Frame Index Mask */ + uint64_t frameIndexMask; + + /** Frame Index Offset */ + int frameIndexOffset; + + /** Packet Index Mask */ + uint64_t packetIndexMask; + + /** Footer offset from start of Packet*/ + int footerOffset; + + /** variable to exclude missing packet */ + bool excludeMissingPackets; + + + //***File parameters*** +#ifdef MYROOT1 + /** Tree where the hits are stored */ + TTree *myTree[MAX_NUMBER_OF_WRITER_THREADS]; + + /** File where the tree is saved */ + TFile *myFile[MAX_NUMBER_OF_WRITER_THREADS]; +#endif + + /** 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 **/ + uint64_t maxFramesPerFile; + const static int progressFrequency = 10; + + /** If file created successfully for all Writer Threads */ + bool fileCreateSuccess; + + /** File header */ + const static unsigned int FILE_HEADER_SIZE = 500; + char fileHeader[MAX_NUMBER_OF_WRITER_THREADS][FILE_HEADER_SIZE]; + + /** File Descriptor */ + FILE *sfilefd[MAX_NUMBER_OF_WRITER_THREADS]; + + + //***acquisition indices/count parameters*** + /** Frame Number of First Frame of an entire Acquisition (including all scans) */ + uint64_t startAcquisitionIndex; + + /** Frame index at start of each real time acquisition (eg. for each scan) */ + uint64_t startFrameIndex; + + /** Actual current frame index of each time acquisition (eg. for each scan) */ + uint64_t frameIndex[MAX_NUMBER_OF_WRITER_THREADS]; + + /** Current Frame Number */ + uint64_t currentFrameNumber[MAX_NUMBER_OF_WRITER_THREADS]; + + /** Previous Frame number from buffer to calculate loss */ + int64_t frameNumberInPreviousFile[MAX_NUMBER_OF_WRITER_THREADS]; + + /** Previous Frame number from last check to calculate loss */ + int64_t frameNumberInPreviousCheck[MAX_NUMBER_OF_WRITER_THREADS]; + + /** total packet count from last check */ + int64_t totalWritingPacketCountFromLastCheck[MAX_NUMBER_OF_WRITER_THREADS]; + + /** Pckets currently in current file, starts new file when it reaches max */ + int64_t lastFrameNumberInFile[MAX_NUMBER_OF_WRITER_THREADS]; + + /** 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]; + + + /* Acquisition started */ + bool acqStarted; + + /* Measurement started - for each thread to get progress print outs*/ + bool measurementStarted[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** Total packet Count listened to by listening threads */ + int totalListeningPacketCount[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** Total packet Count ignored by listening threads */ + int totalIgnoredPacketCount[MAX_NUMBER_OF_LISTENING_THREADS]; + + + + + + + //***receiver parameters*** + /** Receiver Buffer */ + char *buffer[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** Memory allocated */ + char *mem0[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** Circular fifo to point to addresses of data listened to */ + CircularFifo* fifo[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** Circular fifo to point to address already written and freed, to be reused */ + CircularFifo* fifoFree[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** UDP Sockets - Detector to Receiver */ + genericSocket* udpSocket[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** Number of Jobs Per Buffer */ + int numberofJobsPerBuffer; + + /** Total fifo size */ + uint32_t fifoSize; + + /** fifo buffer header size */ + uint32_t fifoBufferHeaderSize; + + /** Dummy Packet identifier value */ + const static uint32_t dummyPacketValue = 0xFFFFFFFF; + + + + //***receiver to GUI parameters*** + /** Current Frame copied for GUI */ + char* latestData[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) */ + uint32_t guiNumPackets[MAX_NUMBER_OF_WRITER_THREADS]; + + /** Semaphore to synchronize Writer and GuiReader 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]; + + + + //***data call back thread parameters*** + /** Ensures if zmq threads created successfully */ + bool zmqThreadStarted; + + /** 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; + + + + //***general and listening thread parameters*** + /** Ensures if threads created successfully */ + bool threadStarted; + + /** Current Thread Index*/ + int currentThreadIndex; + + /** Number of Listening Threads */ + int numberofListeningThreads; + + /** Listening Threads */ + pthread_t listeningThreads[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** Semaphores Synchronizing Listening Threads */ + sem_t listenSemaphore[MAX_NUMBER_OF_LISTENING_THREADS]; + + /** Mask with each bit indicating status of each listening thread */ + volatile uint32_t listeningThreadsMask; + + /** Set to self-terminate listening threads waiting for semaphores */ + bool killAllListeningThreads; + + + + //***writer thread parameters*** + /** Number of Writer Threads */ + int numberofWriterThreads; + + /** Writer Threads */ + pthread_t writingThreads[MAX_NUMBER_OF_WRITER_THREADS]; + + /** Semaphores Synchronizing Writer Threads */ + sem_t writerSemaphore[MAX_NUMBER_OF_WRITER_THREADS]; + + /** Mask with each bit indicating status of each writer thread */ + volatile uint32_t writerThreadsMask; + + /** Mask with each bit indicating file created for each writer thread*/ + volatile uint32_t createFileMask; + + /** Set to self-terminate writer threads waiting for semaphores */ + bool killAllWritingThreads; + + + //***filter parameters*** + /** Common Mode Subtraction Enable FIXME: Always false, only moench uses, Ask Anna */ + bool commonModeSubtractionEnable; + + /** Moench Common Mode Subtraction */ + moenchCommonMode *moenchCommonModeSubtraction; + + /** Single Photon Detector Object for each writer thread */ + singlePhotonDetector *singlePhotonDetectorObject[MAX_NUMBER_OF_WRITER_THREADS]; + + /** Receiver Data Object for each writer thread */ + slsReceiverData *receiverData[MAX_NUMBER_OF_WRITER_THREADS]; + + + + + //***mutex*** + /** Status mutex */ + pthread_mutex_t statusMutex; + + /** Writing mutex */ + pthread_mutex_t writeMutex; + + /** GuiDataReady Mutex */ + pthread_mutex_t dataReadyMutex; + + /** Progress (currentFrameNumber) Mutex */ + pthread_mutex_t progressMutex; + + + //***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 + * 1 callback writes file, we have to open, close it + * 2 we open, close, write file, callback does not do anything */ + int cbAction; + +}; + + +#endif + +//#endif diff --git a/slsReceiverSoftware/include/ansi.h b/slsReceiverSoftware/include/ansi.h new file mode 100644 index 000000000..c28be61d4 --- /dev/null +++ b/slsReceiverSoftware/include/ansi.h @@ -0,0 +1,61 @@ +#define RED "\x1b[31m" +#define GREEN "\x1b[32m" +#define YELLOW "\x1b[33m" +#define BLUE "\x1b[34m" +#define MAGENTA "\x1b[35m" +#define CYAN "\x1b[36m" +#define GRAY "\x1b[37m" +#define DARKGRAY "\x1b[30m" +#define BG_RED "\x1b[41m" +#define BG_GREEN "\x1b[42m" +#define BG_YELLOW "\x1b[43m" +#define BG_BLUE "\x1b[44m" +#define BG_MAGENTA "\x1b[45m" +#define BG_CYAN "\x1b[46m" +#define RESET "\x1b[0m" +#define BOLD "\x1b[1m" + +#define cprintf(code, format, ...) printf(code format RESET, ##__VA_ARGS__) + +/* + +Code examples + +example 1 (a snippet): + + +#ifdef MARTIN + cprintf(BLUE, "LL Write - Len: %2d - If: %X - Data: ",buffer_len, ll->ll_fifo_base); + for (i=0; i < buffer_len/4; i++) + cprintf(BLUE, "%.8X ",*(((unsigned *) buffer)+i)); + printf("\n"); +#endif + +#ifdef MARTIN + cprintf(CYAN, "LL Read - If: %X - Data: ",ll->ll_fifo_base); +#endif + + + +example 2: + +int main() +{ + int i=1; + printf("Normal %i\n", i); + cprintf(RED, "Red\n"); + cprintf(GREEN, "Green\n"); + cprintf(YELLOW, "Yellow\n"); + cprintf(BLUE, "Blue\n"); + cprintf(MAGENTA, "Mangenta %i\n", i); + cprintf(CYAN, "Cyan %i\n", i); + cprintf(BOLD, "White %i\n", i); + cprintf(RED BOLD, "Red %i\n", i); + cprintf(GREEN BOLD, "Green\n"); + cprintf(YELLOW BOLD, "Yellow\n"); + cprintf(BLUE BOLD, "Blue\n"); + cprintf(MAGENTA BOLD, "Mangenta %i\n", i); + cprintf(CYAN BOLD, "Cyan %i\n", i); +} + +*/ diff --git a/slsReceiverSoftware/include/circularFifo.h b/slsReceiverSoftware/include/circularFifo.h new file mode 100644 index 000000000..1d6fd427d --- /dev/null +++ b/slsReceiverSoftware/include/circularFifo.h @@ -0,0 +1,160 @@ +/* CircularFifo.h +* Not any company's property but Public-Domain +* Do with source-code as you will. No requirement to keep this +* header if need to use it/change it/ or do whatever with it +* +* Note that there is No guarantee that this code will work +* and I take no responsibility for this code and any problems you +* might get if using it. The code is highly platform dependent! +* +* Code & platform dependent issues with it was originally +* published at http://www.kjellkod.cc/threadsafecircularqueue +* 2009-11-02 +* @author Kjell Hedstr�m, hedstrom@kjellkod.cc */ + +#ifndef CIRCULARFIFO_H_ +#define CIRCULARFIFO_H_ + +//#include "sls_receiver_defs.h" +#include +#include +#include +using namespace std; + +typedef double double32_t; +typedef float float32_t; +typedef int int32_t; + + + +/** Circular Fifo (a.k.a. Circular Buffer) +* Thread safe for one reader, and one writer */ +template +class CircularFifo { +public: + + CircularFifo(unsigned int Size) : tail(0), head(0){ + Capacity = Size + 1; + array.resize(Capacity); + sem_init(&free_mutex,0,0); + } + virtual ~CircularFifo() { + sem_destroy(&free_mutex); + } + + bool push(Element*& item_); + bool pop(Element*& item_); + + bool isEmpty() const; + bool isFull() const; + + int getSemValue(); + +private: + volatile unsigned int tail; // input index + vector array; + volatile unsigned int head; // output index + unsigned int Capacity; + sem_t free_mutex; + + unsigned int increment(unsigned int idx_) const; +}; + +template +int CircularFifo::getSemValue() +{ + int value; + sem_getvalue(&free_mutex, &value); + return value; +} + + +/** Producer only: Adds item to the circular queue. +* If queue is full at 'push' operation no update/overwrite +* will happen, it is up to the caller to handle this case +* +* \param item_ copy by reference the input item +* \return whether operation was successful or not */ +template +bool CircularFifo::push(Element*& item_) +{ + + //cout<<"*head:"< +bool CircularFifo::pop(Element*& item_) +{ + //cout<<"-tail:"< +bool CircularFifo::isEmpty() const +{ + return (head == tail); +} + +/** Useful for testing and Producer check of status + * Remember that the 'full' status can change quickly + * as the Consumer catches up. + * + * \return true if circular buffer is full. */ +template +bool CircularFifo::isFull() const +{ + int tailCheck = (tail+1) % Capacity; + return (tailCheck == head); +} + +/** Increment helper function for index of the circular queue +* index is inremented or wrapped +* +* \param idx_ the index to the incremented/wrapped +* \return new value for the index */ +template +unsigned int CircularFifo::increment(unsigned int idx_) const +{ + // increment or wrap + // ================= + // index++; + // if(index == array.lenght) -> index = 0; + // + //or as written below: + // index = (index+1) % array.length + idx_ = (idx_+1) % Capacity; + return idx_; +} + +#endif /* CIRCULARFIFO_H_ */ diff --git a/slsReceiverSoftware/include/dummyUDPInterface.h b/slsReceiverSoftware/include/dummyUDPInterface.h new file mode 100644 index 000000000..5cba675cc --- /dev/null +++ b/slsReceiverSoftware/include/dummyUDPInterface.h @@ -0,0 +1,436 @@ +#ifndef DUMMYUDPINTERFACE_H +#define DUMMYUDPINTERFACE_H + +/*********************************************** + * @file UDPInterface.h + * @short Base class with all the functions for the UDP inteface of the receiver + ***********************************************/ +/** + * \mainpage Base class with all the functions for the UDP inteface of the receiver + */ + +/** + * @short Base class with all the functions for the UDP inteface of the receiver + */ + +#include "UDPInterface.h" +#include "sls_receiver_defs.h" +#include "genericSocket.h" + + +class dummyUDPInterface : public UDPInterface { + + + /* abstract class that defines the UDP interface of an sls detector data receiver. + * + * Use the factory method UDPInterface::create() to get an instance: + * + * UDPInterface *udp_interface = UDPInterface::create() + * + * supported sequence of method-calls: + * + * initialize() : once and only once after create() + * + * get*() : anytime after initialize(), multiples times + * set*() : anytime after initialize(), multiple times + * + * startReceiver(): anytime after initialize(). Will fail if state already is 'running' + * + * abort(), + * stopReceiver() : anytime after initialize(). Will do nothing if state already is idle. + * + * getStatus() returns the actual state of the data receiver - running or idle. All other + * get*() and set*() methods access the local cache of configuration values only and *do not* modify the data receiver settings. + * + * Only startReceiver() does change the data receiver configuration, it does pass the whole configuration cache to the data receiver. + * + * get- and set-methods that return a char array (char *) allocate a new array at each call. The caller is responsible to free the allocated space: + * + * char *c = receiver->getFileName(); + * .... + * delete[] c; + * + * always: 1:YES 0:NO for int as bool-like arguments + * + */ + + public: + + /** + * Destructor + */ + dummyUDPInterface() : UDPInterface(), dynamicRange(16), scanTag(1000), nFrames(100), fWrite(1), fOverwrite(1), fIndex(0), fCaught(0), totfCaught(0), startAcqIndex(0), startFrameIndex(0), acqIndex(0), dataCompression(false), period(0), type(slsReceiverDefs::GENERIC), framesNeeded(100), udpPort1(1900), udpPort2(1901), shortFrame(0), nFramesToGui(0), e10G(0) {strcpy(detHostname,"none"); strcpy(fName,"run"); strcpy(fPath,"/scratch/"); strcpy(eth,"eth0"); cout << "New dummy UDP Interface" << endl; + + + + +}; + + ~dummyUDPInterface() {cout << "Destroying dummy UDP Interface" << endl;}; + + + void del(){cout << "Destroying dummy UDP Interface" << endl;}; + + virtual void configure(map config_map) {}; + /** + * Initialize the Receiver + @param detectorHostName detector hostname + * you can call this function only once. You must call it before you call startReceiver() for the first time. + */ + virtual void initialize(const char *detectorHostName){ cout << "set detector hostname to" << detHostname << endl; strcpy(detHostname,detectorHostName);}; + + + /* Returns detector hostname + /returns hostname + * caller needs to deallocate the returned char array. + * if uninitialized, it must return NULL + */ + virtual char *getDetectorHostname() const { cout << "get detector hostname " << detHostname << endl; return (char*) detHostname;}; + + /** + * Returns status of receiver: idle, running or error + */ + virtual slsReceiverDefs::runStatus getStatus() const { cout << "get dsummy status IDLE " << endl; return slsReceiverDefs::IDLE;};; + + /** + * Returns File Name + * caller is responsible to deallocate the returned char array. + */ + virtual char *getFileName() const { cout << "get file name " << fName << endl; return (char*) fName;}; + + + /** + * Returns File Path + * caller is responsible to deallocate the returned char array + */ + virtual char *getFilePath() const { cout << "get file path " << fPath << endl; return (char*) fPath;};; + + + /** + * Returns the number of bits per pixel + */ + virtual int getDynamicRange() const { cout << "get dynamic range " << dynamicRange << endl; return dynamicRange;};; + + /** + * Returns scan tag + */ + virtual int getScanTag() const { cout << "get scan tag " << scanTag << endl; return scanTag;}; + + /* + * Returns number of frames to receive + * This is the number of frames to expect to receiver from the detector. + * The data receiver will change from running to idle when it got this number of frames + */ + virtual int getNumberOfFrames() const { cout << "get number of frames " << nFrames << endl; return nFrames;}; + + /** + * Returns file write enable + * 1: YES 0: NO + */ + virtual int getEnableFileWrite() const { cout << "get enable file write " << fWrite << endl; return fWrite;}; + + /** + * Returns file over write enable + * 1: YES 0: NO + */ + virtual int getEnableOverwrite() const { cout << "get enable file overwrite " << fOverwrite << endl; return fOverwrite;}; + + /** + * Set File Name (without frame index, file index and extension) + @param c file name + /returns file name + * returns NULL on failure (like bad file name) + * does not check the existence of the file - we don't know which path we'll finally use, so no point to check. + * caller is responsible to deallocate the returned char array. + */ + virtual char* setFileName(const char c[]) { strcpy(fName,c); cout << "set file name " << fName << endl; return fName; }; + + /** + * Set File Path + @param c file path + /returns file path + * checks the existence of the directory. returns NULL if directory does not exist or is not readable. + * caller is responsible to deallocate the returned char array. + */ + virtual char* setFilePath(const char c[]) { strcpy(fPath,c); cout << "set file path " << fPath << endl; return fPath; }; + + /** + * Returns the number of bits per pixel + @param dr sets dynamic range + /returns dynamic range + * returns -1 on failure + * FIXME: what are the allowd values - should we use an enum as argument? + */ + virtual int setDynamicRange(const int dr) {dynamicRange=dr; cout << "set dynamic range " << dynamicRange << endl; return dynamicRange; }; + + + /** + * Set scan tag + @param tag scan tag + /returns scan tag (always non-negative) + * FIXME: valid range - only positive? 16bit ore 32bit? + * returns -1 on failure + */ + virtual int setScanTag(const int tag) {scanTag=tag; cout << "set scan tag " << scanTag << endl; return scanTag; }; + + + /** + * Sets number of frames + @param fnum number of frames + /returns number of frames + */ + virtual int setNumberOfFrames(const int fnum) {nFrames=fnum; cout << "set number of frames " << nFrames << endl; return nFrames; }; + + + /** + * Set enable file write + * @param i file write enable + /returns file write enable + */ + virtual int setEnableFileWrite(const int i) {fWrite=i; cout << "set enable file write " << fWrite << endl; return fWrite; }; + + + /** + * Set enable file overwrite + * @param i file overwrite enable + /returns file overwrite enable + */ + virtual int setEnableOverwrite(const int i) {fOverwrite=i; cout << "set enable file overwrite " << fOverwrite << endl; return fOverwrite; }; + + + /** + * Starts Receiver - activate all configuration settings to the eiger receiver and start to listen for packets + @param message is the error message if there is an error + /returns 0 on success or -1 on failure + */ + //FIXME: success == 0 or success == 1? + virtual int startReceiver(char *message=NULL) {cout << "dummy start receiver" << endl; + char buff[8225]; + buff[8224]='\0'; + int ip=0; + int ib; + genericSocket *udpSocket= new genericSocket(50004,genericSocket::UDP,8224); + while((ib=udpSocket->ReceiveDataOnly(buff,8224))>0) { + cout << "*** "<< ib <<" ************************** " << ip++ << endl; + cout << buff << endl; + cout << "*****************************" << endl << endl<< endl ; + } +return 0;}; + + /** + * Stops Receiver - stops listening for packets + /returns success + * same as abort(). Always returns 0. + */ + virtual int stopReceiver() {cout << "dummy stop receiver" << endl; return 0;}; + + /** + * abort acquisition with minimum damage: close open files, cleanup. + * does nothing if state already is 'idle' + */ + virtual void abort() {cout << "Aborting receiver" << endl; }; + + + +/******************************************************************************************************************* + **************************************** Added by Dhanya ********************************************************* + *******************************************************************************************************************/ + + /** + * Returns File Index + */ + virtual int getFileIndex() {cout << "get file index " << fIndex << endl; return fIndex;}; + + /** + * Returns Total Frames Caught for an entire acquisition (including all scans) + */ + virtual int getTotalFramesCaught() {cout << "get total frames caught " << totfCaught << endl ; return totfCaught;}; + + /** + * Returns Frames Caught for each real time acquisition (eg. for each scan) + */ + virtual int getFramesCaught() {cout << "get frames caught " << fCaught << endl; return fCaught;}; + + + /** + * Returns the frame index at start of entire acquisition (including all scans) + */ + virtual uint32_t getStartAcquisitionIndex(){ cout << "get start acquisition index " << startAcqIndex << endl; return startAcqIndex; }; + + /** + * Returns current Frame Index Caught for an entire acquisition (including all scans) + */ + virtual uint32_t getAcquisitionIndex(){ cout << "get acquisition index " << acqIndex << endl; return acqIndex; }; + + + /** + * Returns the frame index at start of each real time acquisition (eg. for each scan) + */ + virtual uint32_t getStartFrameIndex() { cout << "get start frame index " << startFrameIndex << endl; return startFrameIndex; }; + + + /** get data compression, by saving only hits + */ + virtual bool getDataCompression() { cout << "get data compression " << dataCompression << endl; return dataCompression;}; + + /** + * Set receiver type + * @param det detector type + * Returns success or FAIL + */ + virtual int setDetectorType(slsReceiverDefs::detectorType det) {type=det; cout << "set detector type " << det << endl; return slsReceiverDefs::OK;}; + + /** + * Set File Index + * @param i file index + */ + virtual int setFileIndex(int i) {fIndex=i; cout << "get file index " << fIndex << endl; return fIndex;}; + + /** set acquisition period if a positive number + */ + virtual int64_t setAcquisitionPeriod(int64_t index) {if (index>=0) {period=index; cout << "set period " << period << endl;} else { cout << "get period " << period << endl;} return period;}; + + /** + * Set Frame Index Needed + * @param i frame index needed + */ + virtual int setFrameIndexNeeded(int i) {framesNeeded=i; cout << "set frame index needed " << period << endl; return framesNeeded;}; + + /** + * Set UDP Port Number + */ + virtual void setUDPPortNo(int p){udpPort1=p; cout << "set UDP port 1 " << udpPort1 << endl; }; + + + /** + * Set UDP Port Number + */ + virtual void setUDPPortNo2(int p) {udpPort2=p; cout << "set UDP port 2 " << udpPort2 << endl; }; + + /** + * Set Ethernet Interface or IP to listen to + */ + virtual void setEthernetInterface(char* c){strcpy(eth,c); cout << "set eth " << c;}; + + /** + * Set short frame + * @param i if shortframe i=1 + */ + virtual int setShortFrame(int i){shortFrame=i; cout << " set short frame" << shortFrame << endl; return shortFrame;}; + + /** + * Set the variable to send every nth frame to gui + * or if 0,send frame only upon gui request + */ + virtual int setNFrameToGui(int i) {nFramesToGui=i; cout << "set nframes to gui " << nFramesToGui << endl; return nFramesToGui;}; + + /** + * Resets the Total Frames Caught + * This is how the receiver differentiates between entire acquisitions + * Returns 0 + */ + virtual void resetTotalFramesCaught() {totfCaught=0; cout << "total frames caugh reset " << totfCaught << endl;}; + + /** enabl data compression, by saving only hits + /returns if failed + */ + virtual int enableDataCompression(bool enable) {dataCompression=enable; cout << "set data compression " << dataCompression<< endl; return dataCompression;}; + + /** + * enable 10Gbe + @param enable 1 for 10Gbe or 0 for 1 Gbe, -1 to read out + \returns enable for 10Gbe + */ + virtual int enableTenGiga(int enable = -1) {if (enable>=0) {e10G=enable; cout << "set 10Gb "<< e10G << endl;} else cout << "get 10Gb "<< e10G << endl; return e10G;}; + + /** + * Returns the buffer-current frame read by receiver + * @param c pointer to current file name + * @param raw address of pointer, pointing to current frame to send to gui + * @param fnum frame number for eiger as it is not in the packet + * @param startAcquisitionIndex is the start index of the acquisition + * @param startFrameIndex is the start index of the scan + */ + virtual void readFrame(char* c,char** raw, uint32_t &fnum, uint32_t &startAcquisitionIndex, uint32_t &startFrameIndex){cout << "dummy read frame" << endl; }; + + /** set status to transmitting and + * when fifo is empty later, sets status to run_finished + */ + virtual void startReadout(){cout << "dummy start readout" << endl; }; + + /** + * shuts down the udp sockets + * \returns if success or fail + */ + virtual int shutDownUDPSockets(){cout << "dummy shut down udp sockets" << endl; return slsReceiverDefs::OK;}; + + /** + * Closes all files + * @param ithr thread index, -1 for all threads + */ + virtual void closeFile(int ithr = -1){cout << "dummy close file" << ithr << endl; }; + + /** + * Call back for start acquisition + callback arguments are + filepath + filename + fileindex + datasize + + return value is + 0 callback takes care of open,close,wrie file + 1 callback writes file, we have to open, close it + 2 we open, close, write file, callback does not do anything + */ + virtual void registerCallBackStartAcquisition(int (*func)(char*, char*,int, int, void*),void *arg){cout << "dummy register callback start acquisition" << endl; }; + + /** + * Call back for acquisition finished + callback argument is + total frames caught + */ + virtual void registerCallBackAcquisitionFinished(void (*func)(int, void*),void *arg){cout << "dummy register callback acquisition finished" << endl; }; + + /** + * Call back for raw data + args to raw data ready callback are + framenum + datapointer + datasize in bytes + file descriptor + guidatapointer (NULL, no data required) + */ +virtual void registerCallBackRawDataReady(void (*func)(int, char*, int, FILE*, char*, void*),void *arg){cout << "dummy register callback get raw data" << endl; }; + + protected: + + private: + char detHostname[1000]; + char fName[10000]; + char fPath[10000]; + int dynamicRange; + int scanTag; + int nFrames; + int fWrite; + int fOverwrite; + + int fIndex; + int fCaught; +int totfCaught; +int startAcqIndex; +int startFrameIndex; +int acqIndex; +bool dataCompression; + int64_t period; + slsReceiverDefs::detectorType type; + int framesNeeded; + int udpPort1; + int udpPort2; + char eth[1000]; + int shortFrame; + int nFramesToGui; + int e10G; +}; + +#endif /* #ifndef DUMMYUDPINTERFACE_H */ diff --git a/slsReceiverSoftware/include/genericSocket.h b/slsReceiverSoftware/include/genericSocket.h new file mode 100644 index 000000000..1e4f2b32f --- /dev/null +++ b/slsReceiverSoftware/include/genericSocket.h @@ -0,0 +1,735 @@ + +#ifndef GENERIC_SOCKET_H +#define GENERIC_SOCKET_H + + +#include "ansi.h" + + +/** + * + * @libdoc genericSocket provides some functions to open/close sockets both TCP and UDP + * + * @short some functions to open/close sockets both TCP and UDP + * @author Anna Bergamaschi + * @version 0.0 + */ + + + +//version 1.0, base development, Ian 19/01/09 + +/* Modified by anna on 19.01.2009 */ +/* + canceled SetupParameters() and varaibles intialized in the constructors' headers; + defined SEND_REC_MAX_SIZE (for compatibilty with mythen (and possibly other) pure C servers (i would move it to the common header file) + + added #ifndef C_ONLY... to cutout class definition when including in pure C servers (can be removed if SEND_REC_MAX_SIZE is moved to the common header file) + + defined private variables char hostname[1000] and int portno to store connection informations; + + defined public functions int getHostname(char *name) and int getPortNumber() to retrieve connection informations + + added public function int getErrorStatus() returning 1 if socketDescriptor<0 + + remove exits in the constructors and replace them with socketDescriptor=-1 + + replaced the argument of send/receive data with void (to avoid too much casting or compiler errors/warnings) + + added a function which really does not close the socket between send/receive (senddataonly, receivedataonly) +*/ + +#ifdef __CINT__ +//class sockaddr_in; +class socklen_t; +class uint32_t; +class uint32_t_ss; +// CINT view of types: +class sockaddr_in; +// { +// unsigned short int sa_family; +// unsigned char sa_data[14]; +// }; +#else + +#include +#include +#include +#include +#include +#include + +#endif +#include /******exit */ + +#include +#include +#include + +#include +#include +#include + + + +using namespace std; + +#define DEFAULT_PACKET_SIZE 1286 +/*#define SOCKET_BUFFER_SIZE (100*1024*1024) //100MB*/ +#define SOCKET_BUFFER_SIZE (2000*1024*1024) //100MB +#define DEFAULT_PORTNO 1952 +#define DEFAULT_BACKLOG 5 +#define DEFAULT_UDP_PORTNO 50001 +#define DEFAULT_GUI_PORTNO 65000 +#define DEFAULT_ZMQ_PORTNO 70001 + + + +class genericSocket{ + + public: + + /** + Communication protocol +*/ +enum communicationProtocol{ + TCP, /**< TCP/IP */ + UDP /**< UDP */ +}; + + + + genericSocket(const char* const host_ip_or_name, unsigned short int const port_number, communicationProtocol p, int ps = DEFAULT_PACKET_SIZE) : + // portno(port_number), + protocol(p), + is_a_server(0), + socketDescriptor(-1), + file_des(-1), + packet_size(ps), + nsending(0), + nsent(0), + total_sent(0),// sender (client): where to? ip + header_packet_size(0) + { + memset(&serverAddress, 0, sizeof(serverAddress)); + memset(&clientAddress, 0, sizeof(clientAddress)); + // strcpy(hostname,host_ip_or_name); + + strcpy(lastClientIP,"none"); + strcpy(thisClientIP,"none1"); + strcpy(dummyClientIP,"dummy"); + differentClients = 0; + + struct hostent *hostInfo = gethostbyname(host_ip_or_name); + if (hostInfo == NULL){ + cerr << "Exiting: Problem interpreting host: " << host_ip_or_name << "\n"; + } else { + // Set some fields in the serverAddress structure. + serverAddress.sin_family = hostInfo->h_addrtype; + memcpy((char *) &serverAddress.sin_addr.s_addr, hostInfo->h_addr_list[0], hostInfo->h_length); + //((char *) &serverAddress.sin_addr.s_addr)[hostInfo->h_length]='\0'; //a fix for valgrind + serverAddress.sin_port = htons(port_number); + socketDescriptor=0; //You can use send and recv, //would it work????? + } + clientAddress_length=sizeof(clientAddress); + } + + + int getProtocol(communicationProtocol p) { + switch (p) { + case TCP: + return SOCK_STREAM; + break; + case UDP: + return SOCK_DGRAM; + + default: + cerr << "unknow protocol " << p << endl; + return -1; + } + } + + int getProtocol() {return getProtocol(protocol);}; + + + + + /** + The constructor for a server + @short the contructor for a server + \param port_number port number to listen to + \param p TCP or UDP + \param eth interface name or IP address to listen to (if NULL, listen to all interfaces) + + */ + + 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), + socketDescriptor(-1), + file_des(-1), + packet_size(ps), + nsending(0), + nsent(0), + total_sent(0), + header_packet_size(hsize) + { + + memset(&serverAddress, 0, sizeof(serverAddress)); + memset(&clientAddress, 0, sizeof(clientAddress)); +/* // you can specify an IP address: */ +/* // or you can let it automatically select one: */ +/* myaddr.sin_addr.s_addr = INADDR_ANY; */ + + + strcpy(lastClientIP,"none"); + strcpy(thisClientIP,"none1"); + strcpy(dummyClientIP,"dummy"); + differentClients = 0; + + if(serverAddress.sin_port == htons(port_number)){ + socketDescriptor = -10; + return; + } + + char ip[20]; + + strcpy(ip,"0.0.0.0"); + clientAddress_length=sizeof(clientAddress); + if (eth) { + strcpy(ip,nameToIp(string(eth)).c_str()); + if (string(ip)==string("0.0.0.0")) + strcpy(ip,eth); + } + + // strcpy(hostname,"localhost"); //needed?!?!?!? + + + socketDescriptor = socket(AF_INET, getProtocol(),0); //tcp + + if (socketDescriptor < 0) { + cerr << "Can not create socket "<= 0){ \ + close(socketDescriptor); \ + } \ + if(is_a_server and getProtocol() == TCP){\ + if(file_des>0)\ + close(file_des);\ + } + file_des=-1; \ + serverAddress.sin_port=-1; \ + }; + + +/* /\** @short if client returns hostname for connection */ +/* \param name string to write the hostname to */ +/* \returns 0 if client, 1 if server (in this case ignore name return value) */ + +/* *\/ */ +/* int getHostname(char *name){ */ +/* if (is_a_server==0) { */ +/* strcpy(name,getHostname().c_str()); */ +/* } */ +/* return is_a_server; */ +/* }; */ +/* /\** @short if client returns hostname for connection */ +/* \returns hostname */ + +/* *\/ */ +/* string getHostname(){return string(hostname);}; */ + +/* /\** @short returns port number for connection */ +/* \returns port number */ +/* *\/ */ +/* int getPortNumber(){return portno;}; */ + + /** @short returns communication protocol + \returns TCP or UDP + */ + int getCommunicationProtocol(){return protocol;}; + + + /** @short returns error status + \returns 1 if error + */ + int getErrorStatus(){if (socketDescriptor==-10) return -10; else if (socketDescriptor<0) return 1; else return 0;}; + + + /** @short etablishes connection; disconnect should always follow + \returns 1 if error + */ + int Connect(){//cout<<"connect"<0) return file_des; + if (protocol==UDP) return -1; + + if(is_a_server && protocol==TCP){ //server tcp; the server will wait for the clients connection + if (socketDescriptor>0) { + if ((file_des = accept(socketDescriptor,(struct sockaddr *) &clientAddress, &clientAddress_length)) < 0) { + cerr << "Error: with server accept, connection refused"<=0){ + close(socketDescriptor); + socketDescriptor = -1; + } + } + } + + /** @short free connection */ + void Disconnect(){ + if (protocol==UDP){ + close(socketDescriptor); + socketDescriptor=-1; + } + else{ + if(file_des>=0){ //then was open + if(is_a_server){ + close(file_des); + } + else { + //while(!shutdown(socketDescriptor, SHUT_RDWR)); + close(socketDescriptor); + socketDescriptor=-1; + } + file_des=-1; + } + } + }; + + + void ShutDownSocket(){ + while(!shutdown(socketDescriptor, SHUT_RDWR)); + Disconnect(); + }; + + + + /** Set the socket timeout ts is in seconds */ + int SetTimeOut(int ts){ + + + if (ts<=0) + return -1; + + //cout << "socketdescriptor "<< socketDescriptor << endl; + struct timeval tout; + tout.tv_sec = 0; + tout.tv_usec = 0; + if(::setsockopt(socketDescriptor, SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(struct timeval)) <0) + { + cerr << "Error in setsockopt SO_RCVTIMEO "<< 0 << endl; + } + tout.tv_sec = ts; + tout.tv_usec = 0; + if(::setsockopt(socketDescriptor, SOL_SOCKET, SO_SNDTIMEO, &tout, sizeof(struct timeval)) < 0) + { + cerr << "Error in setsockopt SO_SNDTIMEO " << ts << endl; + } + return 0; + + + }; + + + int setPacketSize(int i=-1) { if (i>=0) packet_size=i;return packet_size;}; + + + + static string ipToName(string ip) { + struct ifaddrs *addrs, *iap; + struct sockaddr_in *sa; + char buf[32]; + + strcpy(buf,"none"); + + getifaddrs(&addrs); + for (iap = addrs; iap != NULL; iap = iap->ifa_next) { + if (iap->ifa_addr && (iap->ifa_flags & IFF_UP) && iap->ifa_addr->sa_family == AF_INET) { + sa = (struct sockaddr_in *)(iap->ifa_addr); + inet_ntop(iap->ifa_addr->sa_family, (void *)&(sa->sin_addr), buf, sizeof(buf)); + if (ip==string(buf)) { + //printf("%s\n", iap->ifa_name); + strcpy(buf,iap->ifa_name); + break; + } + } + } + freeifaddrs(addrs); + return string(buf); + }; + + static string nameToMac(string inf) { + struct ifreq ifr; + int sock, j, k; + char mac[32]; + + sock=getSock(inf,&ifr); + + if (-1==ioctl(sock, SIOCGIFHWADDR, &ifr)) { + perror("ioctl(SIOCGIFHWADDR) "); + return string("00:00:00:00:00:00"); + } + for (j=0, k=0; j<6; j++) { + k+=snprintf(mac+k, sizeof(mac)-k-1, j ? ":%02X" : "%02X", + (int)(unsigned int)(unsigned char)ifr.ifr_hwaddr.sa_data[j]); + } + mac[sizeof(mac)-1]='\0'; + + if(sock!=1){ + close(sock); + } + return string(mac); + + }; + + + + static string nameToIp(string inf){ + struct ifreq ifr; + int sock; + char *p, addr[32]; + + sock=getSock(inf,&ifr); + + if (-1==ioctl(sock, SIOCGIFADDR, &ifr)) { + perror("ioctl(SIOCGIFADDR) "); + return string("0.0.0.0"); + } + p=inet_ntoa(((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr); + strncpy(addr,p,sizeof(addr)-1); + addr[sizeof(addr)-1]='\0'; + + if(sock!=1){ + close(sock); + } + return string(addr); + + }; + + static int getSock(string inf, struct ifreq *ifr) { + + int sock; + sock=socket(PF_INET, SOCK_STREAM, 0); + if (-1==sock) { + perror("socket() "); + return 1; + } + strncpy(ifr->ifr_name,inf.c_str(),sizeof(ifr->ifr_name)-1); + ifr->ifr_name[sizeof(ifr->ifr_name)-1]='\0'; + + return sock; + + }; + + + int ReceiveDataOnly(void* buf,int length=0){ + + if (buf==NULL) return -1; + + total_sent=0; + + switch(protocol) { + case TCP: + if (file_des<0) return -1; + while(length>0){ + nsending = (length>packet_size) ? packet_size:length; + nsent = read(file_des,(char*)buf+total_sent,nsending); + if(!nsent) break; + length-=nsent; + total_sent+=nsent; + } + + if (total_sent>0) + strcpy(thisClientIP,dummyClientIP); + + if (strcmp(lastClientIP,thisClientIP)) + differentClients=1; + else + differentClients=0; + + break; + case UDP: + if (socketDescriptor<0) return -1; + //if length given, listens to length, else listens for packetsize till length is reached + if(length){ + + /*int k = 0;*/ + + while(length>0){ + nsending = (length>packet_size) ? packet_size:length; + nsent = recvfrom(socketDescriptor,(char*)buf+total_sent,nsending, 0, (struct sockaddr *) &clientAddress, &clientAddress_length); + if(nsent != nsending){ //if((nsent != nsending)){ && (nsent < packet_size)){ + if(nsent && (nsent != header_packet_size) && (nsent != -1)) + cprintf(RED,"Incomplete Packet size %d\n",nsent); + break; + } + length-=nsent; + total_sent+=nsent; + } + } + //listens to only 1 packet + else{ + //normal + nsending=packet_size; + while(1){ + nsent = recvfrom(socketDescriptor,(char*)buf+total_sent,nsending, 0, (struct sockaddr *) &clientAddress, &clientAddress_length); + if(nsent<=0 || nsent == packet_size) + break; + if(nsent != packet_size && nsent != header_packet_size) + cprintf(RED,"Incomplete Packet size %d\n",nsent); + } + //nsent = 1040; + total_sent+=nsent; + } + break; + default: + ; + } +#ifdef VERY_VERBOSE + cout << "sent "<< total_sent << " Bytes" << endl; +#endif + + + return total_sent; + + + + } + + + int SendDataOnly(void *buf, int length) { +#ifdef VERY_VERBOSE + cout << "want to send "<< length << " Bytes" << endl; +#endif + if (buf==NULL) return -1; + + total_sent=0; + + + switch(protocol) { + case TCP: + if (file_des<0) return -1; + while(length>0){ + nsending = (length>packet_size) ? packet_size:length; + nsent = write(file_des,(char*)buf+total_sent,nsending); + if(!nsent) break; + length-=nsent; + total_sent+=nsent; + } + break; + case UDP: + if (socketDescriptor<0) return -1; + while(length>0){ + nsending = (length>packet_size) ? packet_size:length; + nsent = sendto(socketDescriptor,(char*)buf+total_sent,nsending, 0, (struct sockaddr *) &clientAddress, clientAddress_length); + if(!nsent) break; + length-=nsent; + total_sent+=nsent; + } + + break; + default: + ; + } +#ifdef VERY_VERBOSE + cout << "sent "<< total_sent << " Bytes" << endl; +#endif + return total_sent; + + + } + + + int getCurrentTotalReceived(){ + return total_sent; + } + + char lastClientIP[INET_ADDRSTRLEN]; + char thisClientIP[INET_ADDRSTRLEN]; + int differentClients; + + + protected: + + communicationProtocol protocol; + int is_a_server; + int socketDescriptor; + int file_des; + int packet_size; + struct sockaddr_in clientAddress, serverAddress; + socklen_t clientAddress_length; + char dummyClientIP[INET_ADDRSTRLEN]; + + + private: + + int nsending; + int nsent; + int total_sent; + int header_packet_size; + + + // pthread_mutex_t mp; +}; +#endif diff --git a/slsReceiverSoftware/include/gitInfoReceiver.h b/slsReceiverSoftware/include/gitInfoReceiver.h new file mode 100644 index 000000000..66704494f --- /dev/null +++ b/slsReceiverSoftware/include/gitInfoReceiver.h @@ -0,0 +1,11 @@ +//#define SVNPATH "" +#define SVNURL "git@git.psi.ch:sls_detectors_software/sls_receiver_software.git" +//#define SVNREPPATH "" +#define SVNREPUUID "2f78f4e118bfba443f84a3f72b2839610a8858a3" +//#define SVNREV 0x493 +//#define SVNKIND "" +//#define SVNSCHED "" +#define SVNAUTH "Dhanya_Maliakal" +#define SVNREV 0x493 +#define SVNDATE 0x20161130 +// diff --git a/slsReceiverSoftware/include/gitInfoReceiverTmp.h b/slsReceiverSoftware/include/gitInfoReceiverTmp.h new file mode 100644 index 000000000..58e48f497 --- /dev/null +++ b/slsReceiverSoftware/include/gitInfoReceiverTmp.h @@ -0,0 +1,11 @@ +//#define SVNPATH "" +#define SVNURL "" +//#define SVNREPPATH "" +#define SVNREPUUID "" +//#define SVNREV "" +//#define SVNKIND "" +//#define SVNSCHED "" +#define SVNAUTH "" +#define SVNREV "" +#define SVNDATE "" +// diff --git a/slsReceiverSoftware/include/libzmq.a b/slsReceiverSoftware/include/libzmq.a new file mode 100644 index 000000000..0801ef205 Binary files /dev/null and b/slsReceiverSoftware/include/libzmq.a differ diff --git a/slsReceiverSoftware/include/logger.h b/slsReceiverSoftware/include/logger.h new file mode 100644 index 000000000..a91dea9f4 --- /dev/null +++ b/slsReceiverSoftware/include/logger.h @@ -0,0 +1,246 @@ +//#ifndef __LOG_H__ +//#define __LOG_H__ + +#include +#include +#include +#include +#include + +#ifdef VERBOSE +#define FILELOG_MAX_LEVEL logDEBUG +#endif + +#ifdef VERYVERBOSE +#define FILELOG_MAX_LEVEL logDEBUG4 +#endif + +#ifdef FIFODEBUG +#define FILELOG_MAX_LEVEL logDEBUG5 +#endif + +#ifndef FILELOG_MAX_LEVEL +#define FILELOG_MAX_LEVEL logINFO +#endif + + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define MYCONCAT(x,y) +#define __AT__ string(__FILE__) + string("::") + string(__func__) + string("(): ") +#define __SHORT_FORM_OF_FILE__ \ +(strrchr(__FILE__,'/') \ +? strrchr(__FILE__,'/')+1 \ +: __FILE__ \ +) +#define __SHORT_AT__ string(__SHORT_FORM_OF_FILE__) + string("::") + string(__func__) + string("(): ") + +//":" TOSTRING(__LINE__) + +/* +void error(const char *location, const char *msg){ + printf("Error at %s: %s\n", location, msg); +} +*/ + +inline std::string NowTime(); + +enum TLogLevel {logERROR, logWARNING, logINFO, logDEBUG, logDEBUG1, logDEBUG2, logDEBUG3, logDEBUG4, logDEBUG5}; + +template class Log{ + public: + Log(); + virtual ~Log(); + std::ostringstream& Get(TLogLevel level = logINFO); + static TLogLevel& ReportingLevel(); + static std::string ToString(TLogLevel level); + static TLogLevel FromString(const std::string& level); + protected: + std::ostringstream os; + TLogLevel lev; + private: + Log(const Log&); + Log& operator =(const Log&); +}; + + +class Output2FILE { +public: + static FILE*& Stream(); + static void Output(const std::string& msg); + static void Output(const std::string& msg, TLogLevel level); +}; + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# if defined (BUILDING_FILELOG_DLL) +# define FILELOG_DECLSPEC __declspec (dllexport) +# elif defined (USING_FILELOG_DLL) +# define FILELOG_DECLSPEC __declspec (dllimport) +# else +# define FILELOG_DECLSPEC +# endif // BUILDING_DBSIMPLE_DLL +#else +# define FILELOG_DECLSPEC +#endif // _WIN32 + +class FILELOG_DECLSPEC FILELog : public Log {}; +//typedef Log FILELog; + +#ifdef REST +#define FILE_LOG(level) \ + if (level > FILELOG_MAX_LEVEL) ; \ + else if (level > FILELog::ReportingLevel() || !Output2FILE::Stream()) ; \ + else FILELog().Get(level) +#else + #define FILE_LOG(level) \ + if (level > FILELOG_MAX_LEVEL) ; \ + else if (level > FILELog::ReportingLevel() || !Output2FILE::Stream()) ; \ + else FILELog().Get(level) +#endif + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) + +#include + + + +inline std::string NowTime() + +{ + const int MAX_LEN = 200; + char buffer[MAX_LEN]; + if (GetTimeFormatA(LOCALE_USER_DEFAULT, 0, 0, + "HH':'mm':'ss", buffer, MAX_LEN) == 0) + return "Error in NowTime()"; + + char result[100] = {0}; + static DWORD first = GetTickCount(); + sprintf(result, "%s.%03ld", buffer, (long)(GetTickCount() - first) % 1000); + return result; +} + +#else + +#include + +inline std::string NowTime() +{ + char buffer[11]; + time_t t; + time(&t); + tm r = {0}; + strftime(buffer, sizeof(buffer), "%X", localtime_r(&t, &r)); + struct timeval tv; + gettimeofday(&tv, 0); + char result[100] = {0}; + sprintf(result, "%s.%03ld", buffer, (long)tv.tv_usec / 1000); + return result; +} + +#endif //WIN32 + + +template Log::Log():lev(logDEBUG){} + +template std::ostringstream& Log::Get(TLogLevel level) +{ + lev = level; + os << "- " << NowTime(); + os << " " << ToString(level) << ": "; + os << std::string(level > logDEBUG ? level - logDEBUG : 0, '\t'); + return os; +} + +template Log::~Log() +{ + os << std::endl; +#ifdef REST + T::Output( os.str()); +#else + T::Output( os.str(),lev); +#endif +} + +template TLogLevel& Log::ReportingLevel() +{ + static TLogLevel reportingLevel = logDEBUG5; + return reportingLevel; +} + +template std::string Log::ToString(TLogLevel level) +{ + static const char* const buffer[] = {"ERROR", "WARNING", "INFO", "DEBUG", "DEBUG1", "DEBUG2", "DEBUG3", "DEBUG4","DEBUG5"}; + return buffer[level]; +} + +template +TLogLevel Log::FromString(const std::string& level) +{ + if (level == "DEBUG5") + return logDEBUG5; + if (level == "DEBUG4") + return logDEBUG4; + if (level == "DEBUG3") + return logDEBUG3; + if (level == "DEBUG2") + return logDEBUG2; + if (level == "DEBUG1") + return logDEBUG1; + if (level == "DEBUG") + return logDEBUG; + if (level == "INFO") + return logINFO; + if (level == "WARNING") + return logWARNING; + if (level == "ERROR") + return logERROR; + Log().Get(logWARNING) << "Unknown logging level '" << level << "'. Using INFO level as default."; + return logINFO; +} + + +inline FILE*& Output2FILE::Stream() +{ + static FILE* pStream = stderr; + return pStream; +} + +inline void Output2FILE::Output(const std::string& msg) +{ + FILE* pStream = Stream(); + if (!pStream) + return; + fprintf(pStream, "%s", msg.c_str()); + fflush(pStream); +} + +inline void Output2FILE::Output(const std::string& msg, TLogLevel level) +{ + FILE* pStream = Stream(); + if (!pStream) + return; + switch(level){ + case logERROR: cprintf(RED BOLD,"%s",msg.c_str()); break; + case logWARNING: cprintf(YELLOW BOLD,"%s",msg.c_str()); break; + case logINFO: cprintf(GRAY,"%s",msg.c_str());break; + // case logINFO: cprintf(DARKGRAY BOLD,"%s",msg.c_str());break; + default: fprintf(pStream,"%s",msg.c_str()); break; + } + fflush(pStream); +} + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# if defined (BUILDING_FILELOG_DLL) +# define FILELOG_DECLSPEC __declspec (dllexport) +# elif defined (USING_FILELOG_DLL) +# define FILELOG_DECLSPEC __declspec (dllimport) +# else +# define FILELOG_DECLSPEC +# endif // BUILDING_DBSIMPLE_DLL +#else +# define FILELOG_DECLSPEC +#endif // _WIN32 + + + +//#endif //__LOG_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_ diff --git a/slsReceiverSoftware/include/receiver_defs.h b/slsReceiverSoftware/include/receiver_defs.h new file mode 100755 index 000000000..ec848bf4f --- /dev/null +++ b/slsReceiverSoftware/include/receiver_defs.h @@ -0,0 +1,199 @@ +#ifndef RECEIVER_DEFS_H +#define RECEIVER_DEFS_H + +#include "sls_receiver_defs.h" + +#include + + +/** + * structure of an eiger packet header + * subframenum subframe number for 32 bit mode (already written by firmware) + * missingpacket explicitly put to 0xFF to recognize it in file read (written by software) + * portnum 0 for the first port and 1 for the second port (written by software to file) + * dynamicrange dynamic range or bits per pixel (written by software to file) + */ +typedef struct { + unsigned char subFrameNumber[4]; + unsigned char missingPacket[2]; + unsigned char portIndex[1]; + unsigned char dynamicRange[1]; +} eiger_packet_header_t; +/** + * structure of an eiger packet footer + * framenum 48 bit frame number (already written by firmware) + * packetnum packet number (already written by firmware) + */ +typedef struct { + unsigned char frameNumber[6]; + unsigned char packetNumber[2]; +} eiger_packet_footer_t; + +/** + * structure of an jungfrau packet header + * empty header + * framenumber + * packetnumber + */ +typedef struct { + unsigned char emptyHeader[6]; + unsigned char reserved[4]; + unsigned char packetNumber[1]; + unsigned char frameNumber[3]; + unsigned char bunchid[8]; +} jfrau_packet_header_t; + + +#define GOODBYE -200 + +#define DO_NOTHING 0 +#define CREATE_FILES 1 +#define DO_EVERYTHING 2 + +#define BUF_SIZE (16*1024*1024) //16mb +#define SAMPLE_TIME_IN_NS 100000000//100ms +#define MAX_JOBS_PER_THREAD 1000 +#define HEADER_SIZE_NUM_TOT_PACKETS 4 +#define HEADER_SIZE_NUM_FRAMES 2 +#define HEADER_SIZE_NUM_PACKETS 1 +#define ALL_MASK_32 0xFFFFFFFF + +#define FILE_FRAME_HEADER_LENGTH 16 +#define FILE_HEADER_BUNCHID_OFFSET 8 + + +//all max frames defined in sls_receiver_defs.h. 20000 gotthard, 100000 for short gotthard, 1000 for moench, eiger 20000 + + + +#define GOTTHARD_FIFO_SIZE 25000 //cannot be less than max jobs per thread = 1000 +/*#define GOTTHARD_ALIGNED_FRAME_SIZE 4096*/ +#define GOTTHARD_PACKETS_PER_FRAME 2 +#define GOTTHARD_ONE_PACKET_SIZE 1286 +#define GOTTHARD_ONE_DATA_SIZE 1280 +#define GOTTHARD_BUFFER_SIZE (GOTTHARD_ONE_PACKET_SIZE*GOTTHARD_PACKETS_PER_FRAME) //1286*2 +#define GOTTHARD_DATA_BYTES (GOTTHARD_ONE_DATA_SIZE*GOTTHARD_PACKETS_PER_FRAME) //1280*2 + +#define GOTTHARD_FRAME_INDEX_MASK 0xFFFFFFFE +#define GOTTHARD_FRAME_INDEX_OFFSET 1 +#define GOTTHARD_PACKET_INDEX_MASK 0x1 + +#define GOTTHARD_PIXELS_IN_ROW 1280 +#define GOTTHARD_PIXELS_IN_COL 1 + + +#define GOTTHARD_SHORT_PACKETS_PER_FRAME 1 +#define GOTTHARD_SHORT_ONE_PACKET_SIZE 518 +#define GOTTHARD_SHORT_BUFFER_SIZE 518 +#define GOTTHARD_SHORT_DATABYTES 512 +#define GOTTHARD_SHORT_FRAME_INDEX_MASK 0xFFFFFFFF +#define GOTTHARD_SHORT_FRAME_INDEX_OFFSET 0 +#define GOTTHARD_SHORT_PACKET_INDEX_MASK 0 +#define GOTTHARD_SHORT_PIXELS_IN_ROW 256 +#define GOTTHARD_SHORT_PIXELS_IN_COL 1 + + + + + +#define PROPIX_PIXELS_IN_ROW 22 +#define PROPIX_PIXELS_IN_COL 22 +#define PROPIX_DATABYTES_PER_PIXEL 2 + +#define PROPIX_FIFO_SIZE 25000 //cannot be less than max jobs per thread = 1000 +#define PROPIX_PACKETS_PER_FRAME 2 +#define PROPIX_ONE_PACKET_SIZE 1286 +#define PROPIX_BUFFER_SIZE (PROPIX_ONE_PACKET_SIZE*PROPIX_PACKETS_PER_FRAME) //1286*2 +//#define PROPIX_DATA_BYTES (1280*PROPIX_PACKETS_PER_FRAME) //1280*2 +#define PROPIX_DATA_BYTES (PROPIX_PIXELS_IN_ROW * PROPIX_PIXELS_IN_COL * PROPIX_DATABYTES_PER_PIXEL) //22 * 22 * 2 + +#define PROPIX_FRAME_INDEX_MASK 0xFFFFFFFE +#define PROPIX_FRAME_INDEX_OFFSET 1 +#define PROPIX_PACKET_INDEX_MASK 0x1 + + + + + +#define MOENCH_FIFO_SIZE 2500 //cannot be less than max jobs per thread = 1000 +/*#define MOENCH_ALIGNED_FRAME_SIZE 65536*/ +#define MOENCH_PACKETS_PER_FRAME 40 +#define MOENCH_ONE_PACKET_SIZE 1286 +#define MOENCH_ONE_DATA_SIZE 1280 +#define MOENCH_BUFFER_SIZE (MOENCH_ONE_PACKET_SIZE*MOENCH_PACKETS_PER_FRAME) //1286*40 +#define MOENCH_DATA_BYTES (MOENCH_ONE_DATA_SIZE*MOENCH_PACKETS_PER_FRAME) //1280*40 + +#define MOENCH_FRAME_INDEX_MASK 0xFFFFFF00 +#define MOENCH_FRAME_INDEX_OFFSET 8 +#define MOENCH_PACKET_INDEX_MASK 0xFF + +#define MOENCH_BYTES_PER_ADC (40*2) +#define MOENCH_PIXELS_IN_ONE_ROW 160 +#define MOENCH_BYTES_IN_ONE_ROW (MOENCH_PIXELS_IN_ONE_ROW*2) + + + +#define JFRAU_FIFO_SIZE 2500 //cannot be less than max jobs per thread = 1000 +#define JFRAU_PACKETS_PER_FRAME 128 +#define JFRAU_HEADER_LENGTH 22 +#define JFRAU_ONE_DATA_SIZE 8192 +#define JFRAU_ONE_PACKET_SIZE (JFRAU_HEADER_LENGTH+JFRAU_ONE_DATA_SIZE) //8214 +#define JFRAU_DATA_BYTES (JFRAU_ONE_DATA_SIZE*JFRAU_PACKETS_PER_FRAME) //8192*128 +#define JFRAU_BUFFER_SIZE (JFRAU_ONE_PACKET_SIZE*JFRAU_PACKETS_PER_FRAME) //8214*128 + + +#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 + +#define JFRAU_PIXELS_IN_ONE_ROW (256*4) +#define JFRAU_PIXELS_IN_ONE_COL (256*2) +#define JFRAU_BYTES_IN_ONE_ROW (JFRAU_PIXELS_IN_ONE_ROW*2) + + + +#define JCTB_FIFO_SIZE 2500 //cannot be less than max jobs per thread = 1000 +/*#define MOENCH_ALIGNED_FRAME_SIZE 65536*/ +#define JCTB_PACKETS_PER_FRAME 1 +#define JCTB_ONE_PACKET_SIZE 8224 +#define JCTB_BUFFER_SIZE (JCTB_ONE_PACKET_SIZE*40) +#define JCTB_DATA_BYTES (8192*JCTB_PACKETS_PER_FRAME) + +#define JCTB_FRAME_INDEX_MASK 0xFFFFFFFF +#define JCTB_FRAME_INDEX_OFFSET 6+8 +#define JCTB_PACKET_INDEX_MASK 0xFFFFFFFF + + +#define JCTB_BYTES_PER_ADC (2) +#define JCTB_PIXELS_IN_ONE_ROW 32 +#define JCTB_BYTES_IN_ONE_ROW (JCTB_PIXELS_IN_ONE_ROW*2) + + + + +#define EIGER_MAX_PORTS 2 +#define EIGER_HEADER_PACKET_LENGTH 48 + +#define EIGER_FIFO_SIZE 100 +/*#define EIGER_ALIGNED_FRAME_SIZE 65536*/ +#define EIGER_ONE_GIGA_CONSTANT 16 +#define EIGER_TEN_GIGA_CONSTANT 4 +//#define EIGER_PACKETS_PER_FRAME_COSTANT (16*EIGER_MAX_PORTS)//*bit mode 4*16=64, 8*16=128, 16*16=256, 32*16=512 +#define EIGER_ONE_GIGA_ONE_PACKET_SIZE 1040 +#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_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 + +#define EIGER_FRAME_INDEX_MASK 0xFFFFFFFF //32 bit for now +#define EIGER_FRAME_INDEX_OFFSET 0 +#define EIGER_PACKET_INDEX_MASK 0x0 + +//for each thread +#define EIGER_PIXELS_IN_ONE_ROW (256*2) +#define EIGER_PIXELS_IN_ONE_COL (256) + + +#endif diff --git a/slsReceiverSoftware/include/slsReceiver.h b/slsReceiverSoftware/include/slsReceiver.h new file mode 100644 index 000000000..e76ea7d1b --- /dev/null +++ b/slsReceiverSoftware/include/slsReceiver.h @@ -0,0 +1,91 @@ +/********************************************//** + * @file slsReceiver.h + * @short creates the UDP and TCP class objects + ***********************************************/ +#ifndef SLS_RECEIVER_H +#define SLS_RECEIVER_H + + +#include "slsReceiverTCPIPInterface.h" +#include "UDPInterface.h" + +#include "receiver_defs.h" +#include "MySocketTCP.h" +//#include "utilities.h" + + +/** + *@short creates the UDP and TCP class objects + */ + +class slsReceiver : private virtual slsReceiverDefs { + + public: + /** + * Constructor + * creates the tcp interface and the udp class + * @param argc from command line + * @param argv from command line + * @param succecc socket creation was successfull + */ + slsReceiver(int argc, char *argv[], int &success); + + /** + * Destructor + */ + ~slsReceiver(); + + /** + * starts listening on the TCP port for client comminication + \return 0 for success or 1 for FAIL in creating TCP server + */ + int start(); + + /** + * stops listening to the TCP & UDP port and exit receiver program + */ + void stop(); + + /** + * Close File and exits receiver server + */ + void closeFile(int p); + + /** + * get get Receiver Version + \returns id + */ + int64_t getReceiverVersion(); + + /** + @sort register calbback for starting the acquisition + @param func callback to be called when starting the acquisition. Its arguments are filepath filename fileindex data size + \returns 0 callback takes care of open,close,write file; 1 callback writes file, we have to open, close it; 2 we open, close, write file, callback does not do anything + */ + void registerCallBackStartAcquisition(int (*func)(char*, char*,int, int, void*),void *arg); + + + /** + callback argument is + toatal farmes caught + */ + void registerCallBackAcquisitionFinished(void (*func)(int, void*),void *arg); + + /** + args to raw data ready callback are + framenum + datapointer + datasize in bytes + file descriptor + guidatapointer (NULL, no data required) + */ + void registerCallBackRawDataReady(void (*func)(int, char*, int, FILE*, char*, void*),void *arg); + + + private: + slsReceiverTCPIPInterface* tcpipInterface; + UDPInterface* udp_interface; +}; + + +#endif diff --git a/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h b/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h new file mode 100644 index 000000000..f0bf66f03 --- /dev/null +++ b/slsReceiverSoftware/include/slsReceiverTCPIPInterface.h @@ -0,0 +1,338 @@ +/********************************************//** + * @file slsReceiverTCPIPInterface.h + * @short interface between receiver and client + ***********************************************/ +#ifndef SLS_RECEIVER_TCP_IP_INTERFACE_H +#define SLS_RECEIVER_TCP_IP_INTERFACE_H + + +#include "sls_receiver_defs.h" +#include "receiver_defs.h" +#include "MySocketTCP.h" +#include "UDPInterface.h" + + + +/** + *@short interface between receiver and client + */ + +class slsReceiverTCPIPInterface : private virtual slsReceiverDefs { + + public: + /** + * Constructor + * reads config file, creates socket, assigns function table + * @param succecc socket creation was successfull + * @param rbase pointer to the receiver base + * @param pn port number (defaults to default port number) + */ + + slsReceiverTCPIPInterface(int &success, UDPInterface* rbase, int pn=-1); + + /** + * Sets the port number to listen to. + Take care that the client must know to whcih port it has to listen to, so normally it is better to use a fixes port from the instatiation or change it from the client. + @param pn port number (-1 only get) + \returns actual port number + */ + int setPortNumber(int pn=-1); + + /** + * Starts listening on the TCP port for client comminication + \returns OK or FAIL + */ + int start(); + + /** stop listening on the TCP & UDP port for client comminication */ + void stop(); + + /** Destructor */ + virtual ~slsReceiverTCPIPInterface(); + + /** Close all threaded Files and exit */ + void closeFile(int p); + + /** gets version */ + int64_t getReceiverVersion(); + + //***callback functions*** + /** + * Call back for start acquisition + * callback arguments are + * filepath + * filename + * fileindex + * datasize + * + * return value is the action which decides what the user and default responsibilities to save data are + * 0 callback takes care of open,close,wrie file + * 1 callback writes file, we have to open, close it + * 2 we open, close, write file, callback does not do anything + */ + void registerCallBackStartAcquisition(int (*func)(char*, char*,int, int, void*),void *arg); + + /** + * Call back for acquisition finished + * callback argument is + * total frames caught + */ + void registerCallBackAcquisitionFinished(void (*func)(int, void*),void *arg); + + /** + * Call back for raw data + * args to raw data ready callback are + * framenum + * datapointer + * datasize in bytes + * file descriptor + * guidatapointer (NULL, no data required) + */ + void registerCallBackRawDataReady(void (*func)(int, char*, int, FILE*, char*, void*),void *arg); + +private: + + /** + * Static function - Thread started which is a TCP server + * Called by start() + * @param this_pointer pointer to this object + */ + static void* startTCPServerThread(void *this_pointer); + + + /** + * Thread started which is a TCP server + * Called by start() + */ + void startTCPServer(); + + /** assigns functions to the fnum enum */ + int function_table(); + + /** Decodes Function */ + int decode_function(); + + /** Unrecognized Function */ + int M_nofunc(); + + /** Set detector type */ + int set_detector_type(); + + /** Set File name without frame index, file index and extension */ + int set_file_name(); + + /** Set File path */ + int set_file_dir(); + + /** Set up UDP Details */ + int setup_udp(); + + /** Set File index */ + int set_file_index(); + + /** Set Frame index */ + int set_frame_index(); + + /** Start Receiver - starts listening to udp packets from detector */ + int start_receiver(); + + /** Stop Receiver - stops listening to udp packets from detector*/ + int stop_receiver(); + + /** Gets receiver status */ + int get_status(); + + /** Gets Total Frames Caught */ + int get_frames_caught(); + + /** Gets frame index for each acquisition */ + int get_frame_index(); + + /** Resets Total Frames Caught */ + int reset_frames_caught(); + + /** set short frame */ + int set_short_frame(); + + /** Reads Frame/ buffer */ + int read_frame(); + + /** gotthard specific read frame */ + int gotthard_read_frame(); + + /** propix specific read frame */ + int propix_read_frame(); + + /** moench specific read frame */ + int moench_read_frame(); + + /** eiger specific read frame */ + int eiger_read_frame(); + + /** jungfrau specific read frame */ + int jungfrau_read_frame(); + + /** Sets the receiver to send every nth frame to gui, or only upon gui request */ + int set_read_frequency(); + + /** Sets the timer between frames streamed by receiver when frequency is set to 0 */ + int set_read_receiver_timer(); + + /* Set the data stream enable */ + int set_data_stream_enable(); + + /** Enable File Write*/ + int enable_file_write(); + + /** get version, calls get_version */ + int get_id(); + + /** set status to transmitting and + * when fifo is empty later, sets status to run_finished */ + int start_readout(); + + /** set acquisition period, frame number etc */ + int set_timer(); + + /** enable compression */ + int enable_compression(); + + /** set detector hostname */ + int set_detector_hostname(); + + /** set dynamic range */ + int set_dynamic_range(); + + /** enable overwrite */ + int enable_overwrite(); + + /** enable 10Gbe */ + int enable_tengiga(); + + /** set fifo depth */ + int set_fifo_depth(); + + /** activate/ deactivate */ + int set_activate(); + + /** enable flipped data */ + int set_flipped_data(); + + + //General Functions + /** Locks Receiver */ + int lock_receiver(); + + /** Set port */ + int set_port(); + + /** Get Last Client IP*/ + int get_last_client_ip(); + + /** Updates Client if different clients connect */ + int update_client(); + + /** Sends the updated parameters to client */ + int send_update(); + + /** Exit Receiver Server */ + int exit_server(); + + /** Execute command */ + int exec_command(); + + + + //private: + /** detector type */ + detectorType myDetectorType; + + /** slsReceiverBase object */ + UDPInterface *receiverBase; + + /** Number of functions */ + static const int numberOfFunctions = 256; + + /** Function List */ + int (slsReceiverTCPIPInterface::*flist[numberOfFunctions])(); + + /** Message */ + char mess[MAX_STR_LENGTH]; + + /** success/failure */ + int ret; + + /** Lock Status if server locked to a client */ + int lockStatus; + + /** Short frame */ + int shortFrame; + + /** Packets per frame */ + int packetsPerFrame; + + /** Dynamic Range */ + int dynamicrange; + + /** kill tcp server thread */ + int killTCPServerThread; + + /** thread for TCP server */ + pthread_t TCPServer_thread; + + /** size of one frame*/ + int tenGigaEnable; + + /** port number */ + int portNumber; + + /** Receiver not setup error message */ + char SET_RECEIVER_ERR_MESSAGE[MAX_STR_LENGTH]; + + + //***callback parameters*** + /** + * function being called back for start acquisition + * callback arguments are + * filepath + * filename + * fileindex + * datasize + * + * return value is + * 0 callback takes care of open,close,wrie file + * 1 callback writes file, we have to open, close it + * 2 we open, close, write file, callback does not do anything + */ + int (*startAcquisitionCallBack)(char*, char*,int, int, void*); + void *pStartAcquisition; + + /** + * function being called back for acquisition finished + * callback argument is + * total frames caught + */ + void (*acquisitionFinishedCallBack)(int, void*); + void *pAcquisitionFinished; + + + /** + * function being called back for raw data + * args to raw data ready callback are + * framenum + * datapointer + * datasize in bytes + * file descriptor + * guidatapointer (NULL, no data required) + */ + void (*rawDataReadyCallBack)(int, char*, int, FILE*, char*, void*); + void *pRawDataReady; + + +protected: + /** Socket */ + MySocketTCP* mySock; +}; + + +#endif diff --git a/slsReceiverSoftware/include/slsReceiverUsers.h b/slsReceiverSoftware/include/slsReceiverUsers.h new file mode 100644 index 000000000..eea403560 --- /dev/null +++ b/slsReceiverSoftware/include/slsReceiverUsers.h @@ -0,0 +1,91 @@ +#ifndef SLS_RECEIVER_USERS_H +#define SLS_RECEIVER_USERS_H + +#include +#include + + +class slsReceiver; + + /** +@short Class for implementing the SLS data receiver in the users application. Callbacks can be defined for processing and/or saving data + */ +/** + + + @libdoc slsReceiverUsers is a class that can be instantiated in the users software to receive the data from the detectors. Callbacks can be defined for processing and/or saving data + + + ***********************************************/ + +class slsReceiverUsers { + +public: + /** + * Constructor + * reads config file, creates socket, assigns function table + * @param argc from command line + * @param argv from command line + * @param succecc socket creation was successfull + */ + slsReceiverUsers(int argc, char *argv[], int &success); + + + /** Destructor */ + ~slsReceiverUsers(); + + /** Close File and exits receiver server */ + void closeFile(int p); + + /** + * starts listening on the TCP port for client comminication + \return 0 for success or 1 for FAIL in creating TCP server + */ + int start(); + + /** stops listening to the TCP & UDP port and exit receiver program*/ + void stop(); + + /** + get get Receiver Version + \returns id + */ + int64_t getReceiverVersion(); + + /** + + @sort register calbback for starting the acquisition + \param func callback to be called when starting the acquisition. Its arguments are filepath filename fileindex data size + + \returns 0 callback takes care of open,close,write file; 1 callback writes file, we have to open, close it; 2 we open, close, write file, callback does not do anything + + */ + + void registerCallBackStartAcquisition(int (*func)(char* filepath, char* filename,int fileindex, int datasize, void*),void *arg); + + + /** + @sort register callback for end of acquisition + \param func end of acquisition callback. Argument nf is total frames caught + \returns nothing + */ + + + void registerCallBackAcquisitionFinished(void (*func)(int nf, void*),void *arg); + + + + /** + @sort register callback to be called when data are available (to process and/or save the data). + \param func raw data ready callback. arguments are framenum datapointer datasize file descriptor guidatapointer (NULL, no data required) + \returns nothing + */ + + void registerCallBackRawDataReady(void (*func)(int framenumber, char* datapointer, int datasize, FILE* filedescriptor, char* guidatapointer, void*),void *arg); + + //receiver object + slsReceiver* receiver; +}; + + +#endif diff --git a/slsReceiverSoftware/include/sls_receiver_defs.h b/slsReceiverSoftware/include/sls_receiver_defs.h new file mode 100755 index 000000000..ce0fad9fa --- /dev/null +++ b/slsReceiverSoftware/include/sls_receiver_defs.h @@ -0,0 +1,195 @@ +#ifndef SLS_RECEIVER_DEFS_H +#define SLS_RECEIVER_DEFS_H + + +#ifdef __CINT__ +#define MYROOT +#define __cplusplus +#endif + +#include +#ifdef __cplusplus +#include +#endif +#include "ansi.h" + + +typedef double double32_t; +typedef float float32_t; +typedef int int32_t; + +/** default maximum string length */ +#define MAX_STR_LENGTH 1000 +#define MAX_FRAMES_PER_FILE 20000 +#define SHORT_MAX_FRAMES_PER_FILE 100000 +#define MOENCH_MAX_FRAMES_PER_FILE 1000 +#define EIGER_MAX_FRAMES_PER_FILE 2000 +#define JFRAU_MAX_FRAMES_PER_FILE 10000 +#define JFCTB_MAX_FRAMES_PER_FILE 100000 + + +/** + \file sls_receiver_defs.h +This file contains all the basic definitions common to the slsReceiver class +and to the server programs running on the receiver + * @author Anna Bergamaschi + * @version 0.1alpha (any string) + * @see slsDetector +$Revision: 809 $ + */ + + +#ifdef __cplusplus + +/** @short class containing all the constants and enum definitions */ +class slsReceiverDefs { +public: + + slsReceiverDefs(){}; + +#endif + + /** + Type of the detector + */ + enum detectorType { + GET_DETECTOR_TYPE=-1, /**< the detector will return its type */ + GENERIC, /**< generic sls detector */ + MYTHEN, /**< mythen */ + PILATUS, /**< pilatus */ + EIGER, /**< eiger */ + GOTTHARD, /**< gotthard */ + PICASSO, /**< picasso */ + AGIPD, /**< agipd */ + MOENCH, /**< moench */ + JUNGFRAU, /**< jungfrau */ + JUNGFRAUCTB, /**< jungfrauCTBversion */ + PROPIX /**< propix */ + }; + + + /** + return values + */ + enum { + OK, /**< function succeeded */ + FAIL, /**< function failed */ + FINISHED, /**< acquisition finished */ + FORCE_UPDATE + }; + + + /** + indexes for the acquisition timers + */ + enum timerIndex { + FRAME_NUMBER, /**< number of real time frames: total number of acquisitions is number or frames*number of cycles */ + ACQUISITION_TIME, /**< exposure time */ + FRAME_PERIOD, /**< period between exposures */ + DELAY_AFTER_TRIGGER, /**< delay between trigger and start of exposure or readout (in triggered mode) */ + GATES_NUMBER, /**< number of gates per frame (in gated mode) */ + PROBES_NUMBER, /**< number of probe types in pump-probe mode */ + CYCLES_NUMBER, /**< number of cycles: total number of acquisitions is number or frames*number of cycles */ + ACTUAL_TIME, /**< Actual time of the detector's internal timer */ + MEASUREMENT_TIME, /**< Time of the measurement from the detector (fifo) */ + + PROGRESS, /**< fraction of measurement elapsed - only get! */ + MEASUREMENTS_NUMBER, + FRAMES_FROM_START, + FRAMES_FROM_START_PG, + SAMPLES_JCTB, + SUBFRAME_ACQUISITION_TIME, /**< subframe exposure time */ + MAX_TIMERS + }; + + + /** + staus mask + */ + enum runStatus { + IDLE, /**< detector ready to start acquisition - no data in memory */ + ERROR, /**< error i.e. normally fifo full */ + WAITING, /**< waiting for trigger or gate signal */ + RUN_FINISHED, /**< acquisition not running but data in memory */ + TRANSMITTING, /**< acquisition running and data in memory */ + RUNNING, /**< acquisition running, no data in memory */ + STOPPED /**< acquisition stopped externally */ + }; + +#ifdef __cplusplus + /** returns string from enabled/disabled + \param b true or false + \returns string enabled, disabled + */ + static std::string stringEnable(bool b){\ + if(b) return std::string("enabled"); \ + else return std::string("disabled"); \ + }; + + /** returns detector type string from detector type index + \param t string can be Mythen, Pilatus, Eiger, Gotthard, Agipd, Unknown + \returns MYTHEN, PILATUS, EIGER, GOTTHARD, AGIPD, MÖNCH, GENERIC + */ + static std::string getDetectorType(detectorType t){ \ + switch (t) { \ + case MYTHEN: return std::string("Mythen"); \ + case PILATUS: return std::string("Pilatus"); \ + case EIGER: return std::string("Eiger"); \ + case GOTTHARD: return std::string("Gotthard"); \ + case AGIPD: return std::string("Agipd"); \ + case MOENCH: return std::string("Moench"); \ + case JUNGFRAU: return std::string("Jungfrau"); \ + case JUNGFRAUCTB: return std::string("JungfrauCTB"); \ + case PROPIX: return std::string("Propix"); \ + default: return std::string("Unknown"); \ + }}; + + /** returns detector type index from detector type string + \param type can be MYTHEN, PILATUS, EIGER, GOTTHARD, AGIPD, GENERIC + \returns Mythen, Pilatus, Eiger, Gotthard, Agipd, Mönch, Unknown + */ + static detectorType getDetectorType(std::string const type){\ + if (type=="Mythen") return MYTHEN; \ + if (type=="Pilatus") return PILATUS; \ + if (type=="Eiger") return EIGER; \ + if (type=="Gotthard") return GOTTHARD; \ + if (type=="Agipd") return AGIPD; \ + if (type=="Moench") return MOENCH; \ + if (type=="Jungfrau") return JUNGFRAU; \ + if (type=="JungfrauCTB") return JUNGFRAUCTB; \ + if (type=="Propix") return PROPIX; \ + return GENERIC; \ + }; + + + /** returns string from run status index + \param s can be ERROR, WAITING, RUNNING, TRANSMITTING, RUN_FINISHED + \returns string error, waiting, running, data, finished + */ + static std::string runStatusType(runStatus s){\ + switch (s) { \ + case ERROR: return std::string("error"); \ + case WAITING: return std::string("waiting"); \ + case RUNNING: return std::string("running"); \ + case TRANSMITTING: return std::string("data"); \ + case RUN_FINISHED: return std::string("finished"); \ + case STOPPED: return std::string("stopped"); \ + default: return std::string("idle"); \ + }}; + +#endif + +#ifdef __cplusplus +protected: +#endif + +#ifndef MYROOT +#include "sls_receiver_funcs.h" +#endif + +#ifdef __cplusplus +}; +#endif +; +#endif +; diff --git a/slsReceiverSoftware/include/sls_receiver_funcs.h b/slsReceiverSoftware/include/sls_receiver_funcs.h new file mode 100644 index 000000000..3fffb7d30 --- /dev/null +++ b/slsReceiverSoftware/include/sls_receiver_funcs.h @@ -0,0 +1,65 @@ +/** + @internal + function indexes to call on the server + All set functions with argument -1 work as get, when possible + */ +#ifndef SLS_RECEIVER_FUNCS_H +#define SLS_RECEIVER_FUNCS_H + +enum { + //General functions + F_EXEC_RECEIVER_COMMAND=128, /**< command is executed */ + F_EXIT_RECEIVER, /**< turn off receiver server */ + F_LOCK_RECEIVER, /**< Locks/Unlocks server communication to the given client */ + F_GET_LAST_RECEIVER_CLIENT_IP, /**< returns the IP of the client last connected to the receiver */ + F_SET_RECEIVER_PORT, /**< Changes communication port of the receiver */ + F_UPDATE_RECEIVER_CLIENT, /**< Returns all the important parameters to update the shared memory of the client */ + + // Identification + F_GET_RECEIVER_ID, /**< get receiver id of version */ + F_GET_RECEIVER_TYPE, /**< return receiver type */ + F_SEND_RECEIVER_DETHOSTNAME, /**< set detector hostname to receiver */ + + //network functions + F_RECEIVER_SHORT_FRAME, /**< Sets receiver to receive short frames */ + F_SETUP_RECEIVER_UDP, /**< sets the receiver udp connection and returns receiver mac address */ + + //Acquisition setup functions + F_SET_RECEIVER_TIMER, /**< set/get timer value */ + F_SET_RECEIVER_DYNAMIC_RANGE, /**< set/get detector dynamic range */ + F_READ_RECEIVER_FREQUENCY, /**< sets the frequency of receiver sending frames to gui */ + + // Acquisition functions + F_GET_RECEIVER_STATUS, /**< gets the status of receiver listening mode */ + F_START_RECEIVER, /**< starts the receiver listening mode */ + F_STOP_RECEIVER, /**< stops the receiver listening mode */ + F_START_RECEIVER_READOUT, /**< acquisition has stopped. start remaining readout in receiver */ + F_READ_RECEIVER_FRAME, /**< read one frame to gui*/ + + //file functions + F_SET_RECEIVER_FILE_PATH, /**< sets receiver file directory */ + F_SET_RECEIVER_FILE_NAME, /**< sets receiver file name */ + F_SET_RECEIVER_FILE_INDEX, /**< sets receiver file index */ + F_SET_RECEIVER_FRAME_INDEX, /**< sets the receiver frame index */ + F_GET_RECEIVER_FRAME_INDEX, /**< gets the receiver frame index */ + F_GET_RECEIVER_FRAMES_CAUGHT, /**< gets the number of frames caught by receiver */ + F_RESET_RECEIVER_FRAMES_CAUGHT, /**< resets the frames caught by receiver */ + F_ENABLE_RECEIVER_FILE_WRITE, /**< sets the receiver file write */ + F_ENABLE_RECEIVER_COMPRESSION, /**< enable compression in receiver */ + 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_ACTIVATE, /** < activate/deactivate readout */ + F_STREAM_DATA_FROM_RECEIVER, /**< stream data from receiver to client */ + + F_READ_RECEIVER_TIMER, /** < sets the timer between each data stream in receiver */ + + F_SET_FLIPPED_DATA_RECEIVER /** < sets the enable to flip data across x/y axis (bottom/top) */ + + /* Always append functions hereafter!!! */ +}; + +#endif +/** @endinternal */ diff --git a/slsReceiverSoftware/include/utilities.h b/slsReceiverSoftware/include/utilities.h new file mode 100644 index 000000000..b28d4f32c --- /dev/null +++ b/slsReceiverSoftware/include/utilities.h @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include + +using namespace std; +#include "sls_receiver_defs.h" + +/* uncomment next line to enable debug output */ +//#define EIGER_DEBUG + + +int read_config_file(string fname, int *tcpip_port_no, map * configuration_map); + diff --git a/slsReceiverSoftware/include/zmq.h b/slsReceiverSoftware/include/zmq.h new file mode 100644 index 000000000..78e594f12 --- /dev/null +++ b/slsReceiverSoftware/include/zmq.h @@ -0,0 +1,416 @@ +/* + Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . + + ************************************************************************* + 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/slsReceiverUsers.doxy b/slsReceiverSoftware/slsReceiverUsers.doxy new file mode 100644 index 000000000..b5d37ae18 --- /dev/null +++ b/slsReceiverSoftware/slsReceiverUsers.doxy @@ -0,0 +1,86 @@ +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + + + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = YES + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +INTERNAL_DOCS = NO + +SHOW_INCLUDE_FILES = NO + +SHOW_FILES = NO + +SHOW_NAMESPACES = NO + +COMPACT_LATEX = YES + +PAPER_TYPE = a4 + +PDF_HYPERLINKS = YES + +USE_PDFLATEX = YES + +LATEX_HIDE_INDICES = YES + + +PREDEFINED = __cplusplus + +INPUT = slsReceiver/slsReceiverBase.h + +OUTPUT_DIRECTORY = slsReceiverUsersDocs diff --git a/slsReceiverSoftware/src/.cproject b/slsReceiverSoftware/src/.cproject new file mode 100644 index 000000000..d34dce296 --- /dev/null +++ b/slsReceiverSoftware/src/.cproject @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/slsReceiverSoftware/src/.project b/slsReceiverSoftware/src/.project new file mode 100644 index 000000000..3c7b96561 --- /dev/null +++ b/slsReceiverSoftware/src/.project @@ -0,0 +1,28 @@ + + + Receiver + + + newMythenSoftware + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/slsReceiverSoftware/src/MySocketTCP.cpp b/slsReceiverSoftware/src/MySocketTCP.cpp new file mode 100644 index 000000000..ba9583c1c --- /dev/null +++ b/slsReceiverSoftware/src/MySocketTCP.cpp @@ -0,0 +1,53 @@ + +//version 1.0, base development, Ian 19/01/09 + + +#include "MySocketTCP.h" +#include +#include +#include + +using namespace std; + + + + + + + + + +int MySocketTCP::SendData(void* buf,int length){//length in characters + int ndata = SendDataAndKeepConnection(buf,length); + Disconnect(); + return ndata; +} + +int MySocketTCP::SendDataAndKeepConnection(void* buf,int length){//length in characters + if(last_keep_connection_open_action_was_a_send) Disconnect(); //to keep a structured data flow; + + Connect(); + int total_sent=SendDataOnly(buf,length); + last_keep_connection_open_action_was_a_send=1; + return total_sent; +} + + + + +int MySocketTCP::ReceiveData(void* buf,int length){//length in characters + int ndata = ReceiveDataAndKeepConnection(buf,length); + Disconnect(); + return ndata; +} + +int MySocketTCP::ReceiveDataAndKeepConnection(void* buf,int length){//length in characters + if(!last_keep_connection_open_action_was_a_send) Disconnect(); //to a keep structured data flow; + + Connect(); + // should preform two reads one to receive incomming char count + int total_received=ReceiveDataOnly(buf,length); + last_keep_connection_open_action_was_a_send=0; + return total_received; +} + diff --git a/slsReceiverSoftware/src/UDPBaseImplementation.cpp b/slsReceiverSoftware/src/UDPBaseImplementation.cpp new file mode 100644 index 000000000..e0bf65f6d --- /dev/null +++ b/slsReceiverSoftware/src/UDPBaseImplementation.cpp @@ -0,0 +1,526 @@ +//#ifdef SLS_RECEIVER_UDP_FUNCTIONS +/********************************************//** + * @file UDPBaseImplementation.cpp + * @short does all the functions for a receiver, set/get parameters, start/stop etc. + ***********************************************/ + +#include "UDPBaseImplementation.h" +#include "genericSocket.h" + +#include // stat +#include +#include +using namespace std; + + + +/************************************************************************* + * Constructor & Destructor ********************************************** + * They access local cache of configuration or detector parameters ******* + *************************************************************************/ +UDPBaseImplementation::UDPBaseImplementation(){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + initializeMembers(); + + //***callback parameters*** + startAcquisitionCallBack = NULL; + pStartAcquisition = NULL; + acquisitionFinishedCallBack = NULL; + pAcquisitionFinished = NULL; + rawDataReadyCallBack = NULL; + pRawDataReady = NULL; +} + +void UDPBaseImplementation::initializeMembers(){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + FILE_LOG(logDEBUG) << "Info: Initializing base members"; + //**detector parameters*** + myDetectorType = GENERIC; + strcpy(detHostname,""); + packetsPerFrame = 0; + acquisitionPeriod = 0; + acquisitionTime = 0; + numberOfFrames = 0; + dynamicRange = 16; + tengigaEnable = false; + fifoDepth = 0; + flippedData[0] = 0; + flippedData[1] = 0; + + //***receiver parameters*** + status = IDLE; + activated = true; + + //***connection parameters*** + strcpy(eth,""); + for(int i=0;i 1) return -1; + return flippedData[axis]; +} + + +/***file parameters***/ +char *UDPBaseImplementation::getFileName() const{ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + //not initialized + if(!strlen(fileName)) + return NULL; + + char* output = new char[MAX_STR_LENGTH](); + strcpy(output,fileName); + //freed by calling function + return output; +} + +char *UDPBaseImplementation::getFilePath() const{ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + //not initialized + if(!strlen(filePath)) + return NULL; + + char* output = new char[MAX_STR_LENGTH](); + strcpy(output,filePath); + //freed by calling function + return output; +} + +uint64_t UDPBaseImplementation::getFileIndex() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return fileIndex;} + +int UDPBaseImplementation::getScanTag() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return scanTag;} + +bool UDPBaseImplementation::getFrameIndexEnable() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return frameIndexEnable;} + +bool UDPBaseImplementation::getFileWriteEnable() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return fileWriteEnable;} + +bool UDPBaseImplementation::getOverwriteEnable() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return overwriteEnable;} + +bool UDPBaseImplementation::getDataCompressionEnable() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return dataCompressionEnable;} + +/***acquisition count parameters***/ +uint64_t UDPBaseImplementation::getTotalFramesCaught() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return (totalPacketsCaught/packetsPerFrame);} + +uint64_t UDPBaseImplementation::getFramesCaught() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return (packetsCaught/packetsPerFrame);} + +int64_t UDPBaseImplementation::getAcquisitionIndex() const{ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + if(!totalPacketsCaught) + return -1; + return acquisitionIndex; +} + + +/***connection parameters***/ +uint32_t UDPBaseImplementation::getUDPPortNumber() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return udpPortNum[0];} + +uint32_t UDPBaseImplementation::getUDPPortNumber2() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return udpPortNum[1];} + +char *UDPBaseImplementation::getEthernetInterface() const{ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + char* output = new char[MAX_STR_LENGTH](); + strcpy(output,eth); + //freed by calling function + return output; +} + + +/***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::getFrameToGuiTimer() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return frameToGuiTimerinMS;} + +uint32_t UDPBaseImplementation::getDataStreamEnable() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return dataStreamEnable;} + +uint64_t UDPBaseImplementation::getAcquisitionPeriod() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return acquisitionPeriod;} + +uint64_t UDPBaseImplementation::getAcquisitionTime() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return acquisitionTime;} + +uint64_t UDPBaseImplementation::getNumberOfFrames() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return numberOfFrames;} + +uint32_t UDPBaseImplementation::getDynamicRange() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return dynamicRange;} + +bool UDPBaseImplementation::getTenGigaEnable() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return tengigaEnable;} + +uint32_t UDPBaseImplementation::getFifoDepth() const{ FILE_LOG(logDEBUG) << __AT__ << " starting"; return fifoDepth;} + +/***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;} + + +/************************************************************************* + * Setters *************************************************************** + * They modify the local cache of configuration or detector parameters *** + *************************************************************************/ + +/**initial parameters***/ +void UDPBaseImplementation::configure(map config_map){ + FILE_LOG(logWARNING) << __AT__ << " doing nothing..."; + FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; +} + +void UDPBaseImplementation::setFlippedData(int axis, int enable){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + if(axis<0 || axis>1) return; + flippedData[axis] = enable==0?0:1; + FILE_LOG(logINFO) << "Flipped Data: " << flippedData[0] << " , " << flippedData[1]; +} + + +/***file parameters***/ +void UDPBaseImplementation::setFileName(const char c[]){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + if(strlen(c)) + strcpy(fileName, c); + FILE_LOG(logINFO) << "File name:" << fileName; +} + +void UDPBaseImplementation::setFilePath(const char c[]){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + if(strlen(c)){ + //check if filepath exists + struct stat st; + if(stat(c,&st) == 0) + strcpy(filePath,c); + else{ + strcpy(filePath,""); + FILE_LOG(logWARNING) << "FilePath does not exist:" << filePath; + } + strcpy(filePath, c); + } + FILE_LOG(logDEBUG) << "Info: File path:" << filePath; +} + +void UDPBaseImplementation::setFileIndex(const uint64_t i){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + fileIndex = i; + FILE_LOG(logINFO) << "File Index:" << fileIndex; +} + +//FIXME: needed? +void UDPBaseImplementation::setScanTag(const int i){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + scanTag = i; + FILE_LOG(logINFO) << "Scan Tag:" << scanTag; + +} + +void UDPBaseImplementation::setFrameIndexEnable(const bool b){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + frameIndexEnable = b; + FILE_LOG(logINFO) << "Frame Index Enable: " << stringEnable(frameIndexEnable); +} + +void UDPBaseImplementation::setFileWriteEnable(const bool b){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + fileWriteEnable = b; + FILE_LOG(logINFO) << "File Write Enable: " << stringEnable(fileWriteEnable); +} + +void UDPBaseImplementation::setOverwriteEnable(const bool b){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + overwriteEnable = b; + FILE_LOG(logINFO) << "Overwrite Enable: " << stringEnable(overwriteEnable); +} + +int UDPBaseImplementation::setDataCompressionEnable(const bool b){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + dataCompressionEnable = b; + FILE_LOG(logINFO) << "Data Compression : " << stringEnable(dataCompressionEnable); + + //overridden methods might return FAIL + return OK; +} + + +/***connection parameters***/ +void UDPBaseImplementation::setUDPPortNumber(const uint32_t i){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + udpPortNum[0] = i; + FILE_LOG(logINFO) << "UDP Port Number[0]:" << udpPortNum[0]; +} + +void UDPBaseImplementation::setUDPPortNumber2(const uint32_t i){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + udpPortNum[1] = i; + FILE_LOG(logINFO) << "UDP Port Number[1]:" << udpPortNum[1]; +} + +void UDPBaseImplementation::setEthernetInterface(const char* c){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + strcpy(eth, c); + FILE_LOG(logINFO) << "Ethernet Interface: " << eth; +} + + +/***acquisition parameters***/ +void UDPBaseImplementation::setShortFrameEnable(const int i){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + shortFrameEnable = i; + FILE_LOG(logINFO) << "Short Frame Enable: " << stringEnable(shortFrameEnable); +} + +int UDPBaseImplementation::setFrameToGuiFrequency(const uint32_t freq){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + frameToGuiFrequency = freq; + FILE_LOG(logINFO) << "Frame To Gui Frequency:" << frameToGuiFrequency; + + //overrridden child classes might return FAIL + return OK; +} + +void UDPBaseImplementation::setFrameToGuiTimer(const uint32_t time_in_ms){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + frameToGuiTimerinMS = time_in_ms; + FILE_LOG(logINFO) << "Frame To Gui Timer:" << frameToGuiTimerinMS; +} + + +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"; + + acquisitionPeriod = i; + FILE_LOG(logINFO) << "Acquisition Period:" << (double)acquisitionPeriod/(1E9) << "s"; + + //overrridden child classes might return FAIL + return OK; +} + +int UDPBaseImplementation::setAcquisitionTime(const uint64_t i){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + acquisitionTime = i; + FILE_LOG(logINFO) << "Acquisition Time:" << (double)acquisitionTime/(1E9) << "s"; + + //overrridden child classes might return FAIL + return OK; +} + +int UDPBaseImplementation::setNumberOfFrames(const uint64_t i){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + numberOfFrames = i; + FILE_LOG(logINFO) << "Number of Frames:" << numberOfFrames; + + //overrridden child classes might return FAIL + return OK; +} + +int UDPBaseImplementation::setDynamicRange(const uint32_t i){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + dynamicRange = i; + FILE_LOG(logINFO) << "Dynamic Range:" << dynamicRange; + + //overrridden child classes might return FAIL + return OK; +} + +int UDPBaseImplementation::setTenGigaEnable(const bool b){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + tengigaEnable = b; + FILE_LOG(logINFO) << "Ten Giga Enable: " << stringEnable(tengigaEnable); + + //overridden functions might return FAIL + return OK; +} + +int UDPBaseImplementation::setFifoDepth(const uint32_t i){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + fifoDepth = i; + FILE_LOG(logINFO) << "Fifo Depth: " << i; + + //overridden functions might return FAIL + return OK; +} + +/************************************************************************* + * Behavioral functions*************************************************** + * They may modify the status of the receiver **************************** + *************************************************************************/ + + +/***initial functions***/ +int UDPBaseImplementation::setDetectorType(const detectorType d){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + myDetectorType = d; + //if eiger, set numberofListeningThreads = 2; + FILE_LOG(logINFO) << "Detector Type:" << getDetectorType(d); + return OK; +} + +void UDPBaseImplementation::initialize(const char *c){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + if(strlen(c)) + strcpy(detHostname, c); + FILE_LOG(logINFO) << "Detector Hostname:" << detHostname; +} + + +/***acquisition functions***/ +void UDPBaseImplementation::resetAcquisitionCount(){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + totalPacketsCaught = 0; + FILE_LOG(logINFO) << "totalPacketsCaught:" << totalPacketsCaught; +} + +int UDPBaseImplementation::startReceiver(char *c){ + FILE_LOG(logWARNING) << __AT__ << " doing nothing..."; + FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; + return OK; +} + +void UDPBaseImplementation::stopReceiver(){ + FILE_LOG(logWARNING) << __AT__ << " doing nothing..."; + FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; +} + +void UDPBaseImplementation::startReadout(){ + FILE_LOG(logWARNING) << __AT__ << " doing nothing..."; + FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; +} + +int UDPBaseImplementation::shutDownUDPSockets(){ + FILE_LOG(logWARNING) << __AT__ << " doing nothing..."; + FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; + + //overridden functions might return FAIL + return OK; +} + +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"; +} + + +//FIXME: needed, isnt stopReceiver enough? +void UDPBaseImplementation::abort(){ + FILE_LOG(logWARNING) << __AT__ << " doing nothing..."; + FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; +} + +void UDPBaseImplementation::closeFile(int ithread){ + FILE_LOG(logWARNING) << __AT__ << " doing nothing..."; + FILE_LOG(logERROR) << __AT__ << " must be overridden by child classes"; +} + + +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; + pStartAcquisition=arg; +} + +void UDPBaseImplementation::registerCallBackAcquisitionFinished(void (*func)(int, void*),void *arg){ + acquisitionFinishedCallBack=func; + pAcquisitionFinished=arg; +} + +void UDPBaseImplementation::registerCallBackRawDataReady(void (*func)(int, char*, int, FILE*, char*, void*),void *arg){ + rawDataReadyCallBack=func; + pRawDataReady=arg; +} + + +//#endif diff --git a/slsReceiverSoftware/src/UDPInterface.cpp b/slsReceiverSoftware/src/UDPInterface.cpp new file mode 100644 index 000000000..a6bd2cfac --- /dev/null +++ b/slsReceiverSoftware/src/UDPInterface.cpp @@ -0,0 +1,42 @@ +//#ifdef SLS_RECEIVER_UDP_FUNCTIONS +/********************************************//** + * @file slsReceiverUDPFunctions.cpp + * @short does all the functions for a receiver, set/get parameters, start/stop etc. + ***********************************************/ + + +#include +#include +using namespace std; + + +#include "UDPInterface.h" +#include "UDPBaseImplementation.h" +#include "UDPStandardImplementation.h" +#ifdef REST +#include "UDPRESTImplementation.h" +#endif + + +using namespace std; + +UDPInterface * UDPInterface::create(string receiver_type){ + + if (receiver_type == "standard"){ + FILE_LOG(logINFO) << "Starting " << receiver_type; + return new UDPStandardImplementation(); + } +#ifdef REST + else if (receiver_type == "REST"){ + FILE_LOG(logINFO) << "Starting " << receiver_type; + return new UDPRESTImplementation(); + } +#endif + else{ + FILE_LOG(logWARNING) << "[ERROR] UDP interface not supported, using standard implementation"; + return new UDPBaseImplementation(); + } +} + + +//#endif diff --git a/slsReceiverSoftware/src/UDPRESTImplementation.cpp b/slsReceiverSoftware/src/UDPRESTImplementation.cpp new file mode 100644 index 000000000..df683a946 --- /dev/null +++ b/slsReceiverSoftware/src/UDPRESTImplementation.cpp @@ -0,0 +1,361 @@ +#ifdef SLS_RECEIVER_UDP_FUNCTIONS +/********************************************//** + * @file UDPRESTImplementation.cpp + * @short does all the functions for a receiver, set/get parameters, start/stop etc. + ***********************************************/ + +#include "UDPRESTImplementation.h" + +#include // exit() +#include // set precision +#include // map +#include +#include +#include +#include +//#include "utilities.h" +using namespace std; + +/* + TODO + + filePath != getFilePath + + better state handling. Now it is only IDLE - RUNNING - IDLE + */ + + +UDPRESTImplementation::UDPRESTImplementation(){ + FILE_LOG(logDEBUG) << "PID: " << getpid() << __AT__ << " called"; + + //TODO I do not really know what to do with bottom... + // Default values + isInitialized = true; + rest = NULL; + rest_hostname = "localhost"; + rest_port = 8080; + is_main_receiver = false; +} + + +UDPRESTImplementation::~UDPRESTImplementation(){ + delete rest; +} + + +void UDPRESTImplementation::configure(map config_map){ + // am I ever getting there? + FILE_LOG(logWARNING) << __AT__ << " called"; + + map::const_iterator pos; + + pos = config_map.find("rest_hostname"); + if (pos != config_map.end() ){ + string host_port_str = pos->second; + std::size_t pos = host_port_str.find(":"); // position of "live" in str + if(pos != string::npos){ + istringstream (host_port_str.substr (pos)) >> rest_port; + rest_hostname = host_port_str.substr(0, pos); + } + } + + /* + for(map::const_iterator i=config_map.begin(); i != config_map.end(); i++){ + std::cout << i->first << " " << i->second<< std::endl; + } + */ + +} + + +string UDPRESTImplementation::get_rest_state(RestHelper * rest/*, string *rest_state*/){ + + JsonBox::Value answer; + string rest_state = ""; + + int code = rest->get_json("v1/state", &answer); + if ( code != -1 ){ + rest_state = answer["global_state"].getString(); + } + //rest_state = *prs; + + return rest_state; +} + + +void UDPRESTImplementation::initialize_REST(){ + + FILE_LOG(logDEBUG) << __AT__ << " called"; + FILE_LOG(logDEBUG) << __AT__ << " REST status is initialized: " << isInitialized; + + + string rest_state = ""; + std::string answer = ""; + + // HORRIBLE FIX to get the main receiver + string filename = getFileName(); + if (filename.substr(filename.length() - 2) == "d0"){ + is_main_receiver = true; + } + + if (rest_hostname.empty()) { + FILE_LOG(logDEBUG) << __AT__ <<"can't initialize with empty string or NULL for detectorHostname"; + throw; + } + rest = new RestHelper() ; + rest->init(rest_hostname, rest_port); + rest->set_connection_params(1, 3); + + int code; + + if (!is_main_receiver){ + isInitialized = true; + status = slsReceiverDefs::IDLE; + return; + } + + if (isInitialized == true) { + FILE_LOG(logDEBUG) << __AT__ << "already initialized, can't initialize several times"; + } + else { + FILE_LOG(logDEBUG) << __AT__ << "with receiverHostName=" << rest_hostname << ":" << rest_port; + + try{ + rest_state = get_rest_state(rest); + + if (rest_state == ""){ + FILE_LOG(logERROR) << __AT__ << " REST state returned: " << rest_state; + throw; + } + else{ + isInitialized = true; + status = slsReceiverDefs::IDLE; + } + FILE_LOG(logDEBUG) << __func__ << "Answer: " << answer; + } + catch(std::string e){ + FILE_LOG(logERROR) << __func__ << ": " << e; + throw; + } + + + //JsonBox::Object json_object; + //json_object["configfile"] = JsonBox::Value("FILENAME"); + JsonBox::Value json_request; + //json_request["configfile"] = "config.py"; + json_request["path"] = filePath; + + stringstream ss; + string test; + //std::cout << "GetSTring: " << json_request << std::endl; + json_request.writeToStream(ss, false); + //ss << json_request; + ss >> test; + + rest_state = get_rest_state(rest); + + //code = rest->get_json("v1/state", &answer); + FILE_LOG(logDEBUG) << __AT__ << " state got " << code << " " << answer << "\n"; + if (rest_state != "INITIALIZED"){ + test = "{\"path\":\"" + string( getFilePath() ) + "\", \"n_frames\":10}"; + code = rest->post_json("v1/state/initialize", &answer, test); + } + else{ + test = "{\"path\":\"" + string( getFilePath() ) + "\"}"; + test = "{\"path\":\"" + string( getFilePath() ) + "\", \"n_frames\":10}"; + code = rest->post_json("v1/state/configure", &answer, test); + } + FILE_LOG(logDEBUG) << __AT__ << " state/configure got " << code; + + rest_state = get_rest_state(rest); + + FILE_LOG(logDEBUG) << __AT__ << " state got " << rest_state << "\n"; + + + /* + std::std::cout << string << std::endl; << "---- REST test 3: true, json object "<< std::endl; + JsonBox::Value json_value; + code = rest.get_json("status", &json_value); + std::cout << "JSON " << json_value["status"] << std::endl; + */ + } + FILE_LOG(logDEBUG) << __func__ << ": initialize() done"; +} + + + + + +/** acquisition functions */ + + +int UDPRESTImplementation::startReceiver(char message[]){ + int i; + + FILE_LOG(logDEBUG) << __FILE__ << "::" << __func__ << " starting"; + initialize_REST(); + FILE_LOG(logDEBUG) << __FILE__ << "::" << __func__ << " initialized"; + + std::string answer; + int code; + //char *intStr = itoa(a); + //string str = string(intStr); + // TODO: remove hardcode!!! + stringstream ss; + ss << getDynamicRange(); + string str_dr = ss.str(); + stringstream ss2; + ss2 << getNumberOfFrames(); + string str_n = ss2.str(); + + + string rest_state = ""; + std::string request_body = "{\"settings\": {\"bit_depth\": " + str_dr + ", \"n_frames\": " + str_n + "}}"; + //std::string request_body = "{\"settings\": {\"nimages\":1, \"scanid\":999, \"bit_depth\":16}}"; + if(is_main_receiver){ + FILE_LOG(logDEBUG) << __FILE__ << "::" << " sending this configuration body: " << request_body; + code = rest->post_json("v1/state/configure", &answer, request_body); + code = rest->get_json("v1/state", &answer); + FILE_LOG(logDEBUG) << __FILE__ << "::" << " got: " << answer; + + rest_state = get_rest_state(rest); + + code = rest->post_json("v1/state/open", &answer); + } + + status = RUNNING; + + return OK; +} + + + + +void UDPRESTImplementation::stopReceiver(){ + + FILE_LOG(logDEBUG) << __AT__ << "called"; + + if(status == RUNNING) + startReadout(); + + while(status == TRANSMITTING) + usleep(5000); + + //change status + status = IDLE; + + FILE_LOG(logDEBUG) << __AT__ << "exited, status " << endl; + +} + + + + + +void UDPRESTImplementation::startReadout(){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + status = TRANSMITTING; + + //kill udp socket to tell the listening thread to push last packet + shutDownUDPSockets(); + FILE_LOG(logDEBUG) << __FILE__ << "::" << __func__ << " done"; + +} + + + + + +/* FIXME + * Its also called by TCP in case of illegal shut down such as Ctrl + c. + * Upto you what you want to do with it. + */ +int UDPRESTImplementation::shutDownUDPSockets(){ + + FILE_LOG(logDEBUG) << __AT__ << "called"; + + // this is just to be sure, it could be removed + /* + for(int i=0;iShutDownSocket(); + delete udpSocket[i]; + udpSocket[i] = NULL; + } + } + */ + JsonBox::Value answer; + int code; + string rest_state = ""; + + //FILE_LOG(logDEBUG) << __AT__ << " numListeningThreads=" << numListeningThreads; + if (rest == NULL){ + FILE_LOG(logWARNING) << __AT__ << "No REST object initialized, closing..."; + return OK; + } + + // getting the state + if (is_main_receiver){ + + FILE_LOG(logWARNING) << "PLEASE WAIT WHILE CHECKING AND SHUTTING DOWN ALL CONNECTIONS!"; + rest_state = get_rest_state(rest); + + while (rest_state != "OPEN"){ + rest_state = get_rest_state(rest); + usleep(10000); + } + //while (rest_state != "TRANSIENT"){ + // rest_state = get_rest_state(rest); + // usleep(10000); + //} + + code = rest->post_json("v1/state/close", &answer); + code = rest->post_json("v1/state/reset", &answer); + + //rest_state = get_rest_state(rest); + //std::cout << rest_state << std::endl; + } + status = slsReceiverDefs::RUN_FINISHED; + + //LEO: not sure it's needed + //delete rest; + + FILE_LOG(logDEBUG) << __AT__ << "finished"; + return OK; +} + + + +/* FIXME + * do you really need this, this is called if registerDataCallback() is activated + * in your gui to get data from receiver. you probably have a different way + * of reconstructing complete data set from all receivers + */ +void UDPRESTImplementation::readFrame(char* c,char** raw, uint64_t &startAcq, uint64_t &startFrame){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + strcpy(c,""); + *raw = NULL; +} + + + + + +/* FIXME + * Its called by TCP in case of illegal shut down such as Ctrl + c. + * Upto you what you want to do with it. + */ +void UDPRESTImplementation::closeFile(int ithr){ + FILE_LOG(logDEBUG) << __AT__ << "called for thread " << ithr; + FILE_LOG(logDEBUG) << __AT__ << "exited for thread " << ithr; +} + + +uint64_t UDPRESTImplementation::getTotalFramesCaught() const{ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + return (0); +} + + + +#endif diff --git a/slsReceiverSoftware/src/UDPStandardImplementation.cpp b/slsReceiverSoftware/src/UDPStandardImplementation.cpp new file mode 100644 index 000000000..79d8e0520 --- /dev/null +++ b/slsReceiverSoftware/src/UDPStandardImplementation.cpp @@ -0,0 +1,3465 @@ +/********************************************//** + * @file UDPStandardImplementation.cpp + * @short does all the functions for a receiver, set/get parameters, start/stop etc. + ***********************************************/ + + +#include "UDPStandardImplementation.h" + +#include "moench02ModuleData.h" +#include "gotthardModuleData.h" +#include "gotthardShortModuleData.h" + +#include // exit() +#include //set precision for printing parameters for create new file +#include //map +#include +#include +#include +#include + +#include //zmq +#include + +using namespace std; + +#define WRITE_HEADERS + +/************************************************************************* + * Constructor & Destructor ********************************************** + * They access local cache of configuration or detector parameters ******* + *************************************************************************/ + +UDPStandardImplementation::UDPStandardImplementation(){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + initializeMembers(); + + //***mutex*** + pthread_mutex_init(&statusMutex,NULL); + pthread_mutex_init(&writeMutex,NULL); + pthread_mutex_init(&dataReadyMutex,NULL); + pthread_mutex_init(&progressMutex,NULL); + + //to increase socket receiver buffer size and max length of input queue by changing kernel settings + if(myDetectorType == EIGER); + else if(system("echo $((100*1024*1024)) > /proc/sys/net/core/rmem_max")){ + FILE_LOG(logDEBUG) << "Warning: No root permission to change socket receiver buffer size in file /proc/sys/net/core/rmem_max"; + }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 + sysctl -p + // from the manual + sysctl -w net.core.rmem_max=16777216 + sysctl -w net.core.netdev_max_backlog=250000 + */ +} + +UDPStandardImplementation::~UDPStandardImplementation(){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + for(int i=0;i(receiverData[i], csize, sigma, sign, moenchCommonModeSubtraction); +} + + + + +int UDPStandardImplementation::setupFifoStructure(){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + + //number of jobs per buffer + int64_t i; + int oldNumberofJobsPerBuffer = numberofJobsPerBuffer; + //eiger always listens to 1 packet at a time + if(excludeMissingPackets){ + numberofJobsPerBuffer = 1; + FILE_LOG(logDEBUG) << "Info: 1 packet per buffer"; + } + //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; + //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 + if(acquisitionPeriod) + i = SAMPLE_TIME_IN_NS/acquisitionPeriod; + else{ + if(acquisitionTime) + i = SAMPLE_TIME_IN_NS/acquisitionTime; + else + i = SAMPLE_TIME_IN_NS; + } + //max frames to listen to at a time is limited by 1000 + if (i > MAX_JOBS_PER_THREAD) + numberofJobsPerBuffer = MAX_JOBS_PER_THREAD; + else if (i < 1) + numberofJobsPerBuffer = 1; + else + numberofJobsPerBuffer = i; + + } + FILE_LOG(logINFO) << "Number of Frames per buffer:" << numberofJobsPerBuffer << endl; + } + + + + // fifo depth + uint32_t oldFifoSize = fifoSize; + + + //reduce fifo depth if > 1 numberofJobsPerBuffer + if(fifoDepth % numberofJobsPerBuffer) + fifoSize = (fifoDepth/numberofJobsPerBuffer)+1; + else + fifoSize = fifoDepth/numberofJobsPerBuffer; + + //do not rebuild fifo structure if it is the same (oldfifosize differs only for different packetsperframe) + if((oldNumberofJobsPerBuffer == numberofJobsPerBuffer) && (oldFifoSize == fifoSize)) + return OK; + FILE_LOG(logINFO) << "Info: Total Fifo Size:" << fifoSize; + + + + //delete threads + if(threadStarted){ + createListeningThreads(true); + createWriterThreads(true); + } + + + //set up fifo structure + for(int i=0;iisEmpty()){ + fifoFree[i]->pop(buffer[i]); + //cprintf(BLUE,"FifoFree[%d]: value:%d, pop 0x%x\n",i,fifoFree[i]->getSemValue(),(void*)(buffer[i])); + } + delete fifoFree[i]; + fifoFree[i] = 0; + } + if(fifo[i]){ + while(!fifo[i]->isEmpty()){ + fifo[i]->pop(buffer[i]); + //cprintf(CYAN,"Fifo[%d]: value:%d, pop 0x%x\n",i,fifo[i]->getSemValue(),(void*)(buffer[i])); + } + delete fifo[i]; + fifo[i] = 0; + } + if(mem0[i]){ + free(mem0[i]); + mem0[i] = 0; + } + + } + + + for(int i=0;i(fifoSize); + fifo[i] = new CircularFifo(fifoSize); + + //allocate memory + mem0[i] = (char*)calloc((bufferSize * numberofJobsPerBuffer + fifoBufferHeaderSize) * fifoSize,sizeof(char)); + if (mem0[i] == NULL){ + cprintf(BG_RED,"Error: Could not allocate memory for listening \n"); + return FAIL; + } + + //push free address into fifoFree + buffer[i]=mem0[i]; + while (buffer[i] < (mem0[i]+(bufferSize * numberofJobsPerBuffer + fifoBufferHeaderSize) * (fifoSize-1))) { + //cprintf(BLUE,"fifofree %d: push 0x%p\n",i,(void*)buffer[i]); + /*for(int k=0;kpush(buffer[i])); + //cprintf(GREEN,"Fifofree[%d]: value:%d, push 0x%x\n",i,fifoFree[i]->getSemValue(),(void*)(buffer[i])); +#ifdef DEBUG5 + cprintf(BLUE,"Info: %d fifostructure free pushed into fifofree %p\n", i, (void*)(buffer[i])); +#endif + buffer[i] += (bufferSize * numberofJobsPerBuffer + fifoBufferHeaderSize); + } + } + cout << "Fifo structure(s) reconstructed" << endl; + + //create threads + if(createListeningThreads() == FAIL){ + FILE_LOG(logERROR) << "Could not create listening thread"; + return FAIL; + } + if(createWriterThreads() == FAIL){ + FILE_LOG(logERROR) << "Could not create writer threads"; + return FAIL; + } + setThreadPriorities(); + + return OK; +} + + + + + + + + +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; + } + + FILE_LOG(logINFO) << "File name:" << fileName; +} + +int UDPStandardImplementation::setDataCompressionEnable(const bool b){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + if(myDetectorType != EIGER){ + cout << "Info: Setting up Data Compression Enable to " << stringEnable(b); +#ifdef MYROOT1 + cout << " WITH ROOT" << endl; +#else + cout << " WITHOUT ROOT" << endl; +#endif + } + + //set data compression enable + dataCompressionEnable = b; + + //-- create writer threads depending on enable + pthread_mutex_lock(&statusMutex); + writerThreadsMask = 0x0; + pthread_mutex_unlock(&(statusMutex)); + + createWriterThreads(true); + if(b) + numberofWriterThreads = MAX_NUMBER_OF_WRITER_THREADS; + else + numberofWriterThreads = 1; + if(createWriterThreads() == FAIL){ + cprintf(BG_RED,"Error: Could not create writer threads\n"); + return FAIL; + } + //-- end of create writer threads + setThreadPriorities(); + + //filter + deleteFilter(); + if(b) + initializeFilter(); + + FILE_LOG(logINFO) << "Data Compression: " << stringEnable(dataCompressionEnable); + + return OK; +} + +/***acquisition count parameters***/ +uint64_t UDPStandardImplementation::getTotalFramesCaught() const{ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + return (totalPacketsCaught/(packetsPerFrame*numberofListeningThreads)); +} + +uint64_t UDPStandardImplementation::getFramesCaught() const{ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + return (packetsCaught/(packetsPerFrame*numberofListeningThreads)); +} + +/***acquisition parameters***/ +void UDPStandardImplementation::setShortFrameEnable(const int i){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + shortFrameEnable = i; + + if(shortFrameEnable!=-1){ + bufferSize = GOTTHARD_SHORT_BUFFER_SIZE; + onePacketSize = GOTTHARD_SHORT_BUFFER_SIZE; + oneDataSize = GOTTHARD_SHORT_DATABYTES; + 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{ + bufferSize = GOTTHARD_BUFFER_SIZE; + onePacketSize = GOTTHARD_ONE_PACKET_SIZE; + oneDataSize = GOTTHARD_ONE_DATA_SIZE; + maxFramesPerFile = MAX_FRAMES_PER_FILE; + packetsPerFrame = GOTTHARD_PACKETS_PER_FRAME; + frameIndexMask = GOTTHARD_FRAME_INDEX_MASK; + frameIndexOffset = GOTTHARD_FRAME_INDEX_OFFSET; + packetIndexMask = GOTTHARD_PACKET_INDEX_MASK; + } + + //filter + deleteFilter(); + if(dataCompressionEnable) + initializeFilter(); + + FILE_LOG(logINFO) << "Short Frame Enable: " << shortFrameEnable; +} + + +int UDPStandardImplementation::setFrameToGuiFrequency(const uint32_t freq){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + frameToGuiFrequency = freq; + if(setupFifoStructure() == FAIL) + return FAIL; + + FILE_LOG(logINFO) << "Frame to Gui Frequency: " << frameToGuiFrequency; + + return OK; +} + + + +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; + + + if(oldvalue!=dataStreamEnable){ + //data sockets have to be created again as the client ones are + if(zmqThreadStarted) + createDataCallbackThreads(true); + + if(dataStreamEnable){ + numberofDataCallbackThreads = numberofListeningThreads; + if(createDataCallbackThreads() == FAIL){ + cprintf(BG_RED,"Error: Could not create data callback threads\n"); + } + } + } + + return OK; +} + + + +int UDPStandardImplementation::setAcquisitionPeriod(const uint64_t i){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + acquisitionPeriod = i; + if(setupFifoStructure() == FAIL) + return FAIL; + + FILE_LOG(logINFO) << "Acquisition Period: " << (double)acquisitionPeriod/(1E9) << "s"; + + if(myDetectorType == EIGER) + for(int i=0; iShutDownSocket(); + FILE_LOG(logINFO) << "Shut down UDP Socket " << i; + delete udpSocket[i]; + udpSocket[i] = 0; + } + } + return OK; +} + + + + +/** + * Pre: status is running, udp sockets have been initialized, stop receiver initiated + * Post:udp sockets closed, status is transmitting + * */ +void UDPStandardImplementation::startReadout(){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + FILE_LOG(logDEBUG) << "Transmitting last data"; + + if(status == RUNNING){ + + + //needs to wait for packets only if activated + if(activated){ + //check if all packets got + int totalP = 0,prev=-1; + for(int i=0; igetCurrentTotalReceived(); + + //wait for all packets + if((unsigned long long int)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 prevP:%d totalP:%d PrevBuffer:%d currentBuffer:%d\n",prev,totalP,prevReceivedInBuffer,currentReceivedInBuffer); + +#endif + //usleep(2*1000*1000); + usleep(5*1000);/* Need to find optimal time (exposure time and acquisition period) **/ + prev = totalP; + totalP = 0; + for(int i=0; igetCurrentTotalReceived(); +#ifdef DEBUG5 + cprintf(MAGENTA,"\tupdated: totalP:%d currently in buffer:%d\n",totalP,currentReceivedInBuffer); + +#endif + } + + } + } + + //set status + pthread_mutex_lock(&statusMutex); + status = TRANSMITTING; + pthread_mutex_unlock(&statusMutex); + + FILE_LOG(logINFO) << "Status: Transmitting"; + } + + //shut down udp sockets and make listeners push dummy (end) packets for writers + shutDownUDPSockets(); +} + + + +/**make this better by asking all of it at once*/ +void UDPStandardImplementation::readFrame(int ithread, char* c,char** raw, int64_t &startAcq, int64_t &startFrame){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + +} + + + +void UDPStandardImplementation::closeFile(int ithread){ + FILE_LOG(logDEBUG) << __AT__ << " called for " << ithread ; + + //normal + if(!dataCompressionEnable){ + if(sfilefd[ithread]){ +#ifdef DEBUG4 + FILE_LOG(logDEBUG4) << "Going to close file: " << fileno(sfilefd)); +#endif + fflush(sfilefd[ithread]); + fclose(sfilefd[ithread]); + sfilefd[ithread] = 0; + } + } + + //compression + else{ +#if (defined(MYROOT1) && defined(ALLFILE_DEBUG)) || !defined(MYROOT1) + if(sfilefd[0]){ +#ifdef DEBUG4 + FILE_LOG(logDEBUG4) << "sfilefd: " << (int)sfilefd[i]; +#endif + fclose(sfilefd[0]); + sfilefd[0] = 0; + } +#endif + +#ifdef MYROOT1 + pthread_mutex_lock(&writeMutex); + //write to file + if(myTree[ithread] && myFile[ithread]){ + myFile[ithread] = myTree[ithread]->GetCurrentFile(); + + if(myFile[ithread]->Write()) + //->Write(tall->GetName(),TObject::kOverwrite); + cout << "Thread " << ithread <<": wrote frames to file" << endl; + else + cout << "Thread " << ithread << ": could not write frames to file" << endl; + + }else + cout << "Thread " << ithread << ": could not write frames to file: No file or No Tree" << endl; + //close file + if(myTree[ithread] && myFile[ithread]) + myFile[ithread] = myTree[ithread]->GetCurrentFile(); + if(myFile[ithread] != 0) + myFile[ithread]->Close(); + myFile[ithread] = 0; + myTree[ithread] = 0; + pthread_mutex_unlock(&writeMutex); + +#endif + } +} + + +//eiger only +int UDPStandardImplementation::setActivate(int enable){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + if(enable != -1){ + activated = enable; + FILE_LOG(logINFO) << "Activation: " << stringEnable(activated); + } + + for(int i=0; igetErrorStatus(); + if(!iret){ + cout << "UDP port opened at port " << port[i] << endl; + }else{ + FILE_LOG(logERROR) << "Could not create UDP socket on port " << port[i] << " error: " << iret; + shutDownUDPSockets(); + return FAIL; + } + } + + FILE_LOG(logDEBUG) << "UDP socket(s) created successfully."; + cout << "Listener Ready ..." << endl; + + return OK; +} + + + +int UDPStandardImplementation::setupWriter(){ + FILE_LOG(logDEBUG) << __AT__ << " starting"; + + //acquisition start call back returns enable write + cbAction = DO_EVERYTHING; + if (startAcquisitionCallBack) + cbAction=startAcquisitionCallBack(filePath,fileNamePerThread[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."; + if (rawDataReadyCallBack){ + FILE_LOG(logINFO) << "Data Write has been defined externally"; + } + }else if(!fileWriteEnable){ + FILE_LOG(logINFO) << "Data will not be saved"; + } + + + //creating first file + //setting all value to 1 + pthread_mutex_lock(&statusMutex); + for(int i=0; i DO_NOTHING){ + + //close file pointers + if(sfilefd[ithread]){ + //all threads need to close file, reset mask and exit loop + if(myDetectorType == EIGER && fileWriteEnable && (cbAction > DO_NOTHING)){ + updateFileHeader(ithread); + fseek(sfilefd[ithread],0,0); + fwrite((void*)fileHeader[ithread], 1, FILE_HEADER_SIZE, sfilefd[ithread]); + } + fflush(sfilefd[ithread]); + fclose(sfilefd[ithread]); + sfilefd[ithread] = 0; + } + + //create file + if(!overwriteEnable){ + if (NULL == (sfilefd[ithread] = fopen((const char *) (completeFileName[ithread]), "wx"))){ + FILE_LOG(logERROR) << "Could not create/overwrite file" << completeFileName[ithread]; + sfilefd[ithread] = 0; + return FAIL; + } + }else if (NULL == (sfilefd[ithread] = fopen((const char *) (completeFileName[ithread]), "w"))){ + FILE_LOG(logERROR) << "Could not create file" << completeFileName[ithread]; + sfilefd[ithread] = 0; + return FAIL; + } + //setting file buffer size to 16mb + setvbuf(sfilefd[ithread],NULL,_IOFBF,BUF_SIZE); + + //Print packet loss and filenames + if(totalWritingPacketCount[ithread]){ + if(numberofWriterThreads>1){ + cprintf(BLUE,"File:%s" + "\nThread:%d" + "\tLost:%lld" + "\t\tPackets:%lld" + "\tFrame#:%lld" + "\tPFrame#:%lld\n", + completeFileName[ithread],ithread, + ((frameNumberInPreviousFile[ithread]+1+maxFramesPerFile)>numberOfFrames) + ?(long long int)((numberOfFrames-(frameNumberInPreviousFile[ithread]+1))*packetsPerFrame - totalPacketsInFile[ithread]) + :(long long int)((frameNumberInPreviousFile[ithread]+maxFramesPerFile - frameNumberInPreviousFile[ithread])*packetsPerFrame - totalPacketsInFile[ithread]), + (long long int)totalPacketsInFile[ithread], + (long long int)currentFrameNumber[ithread], + (long long int)frameNumberInPreviousFile[ithread] + ); + }else{ + cprintf(BLUE,"File:%s" + "\nLost:%lld" + "\t\tPackets:%lld" + "\tFrame#:%lld" + "\tPFrame#:%lld\n", + completeFileName[ithread], + ((frameNumberInPreviousFile[ithread]+1+maxFramesPerFile)>numberOfFrames) + ?(long long int)(numberOfFrames-(frameNumberInPreviousFile[ithread]+1)) + :(long long int)(frameNumberInPreviousFile[ithread]+maxFramesPerFile - frameNumberInPreviousFile[ithread]), + (long long int)totalPacketsInFile[ithread], + (long long int)currentFrameNumber[ithread], + (long long int)frameNumberInPreviousFile[ithread] + ); + } + + }else + printf("Thread:%d File opened:%s\n",ithread, completeFileName[ithread]); + + //write file header + if(myDetectorType == EIGER) + fwrite((void*)fileHeader[ithread], 1, FILE_HEADER_SIZE, sfilefd[ithread]); + } + + //reset counters for each new file + if(totalWritingPacketCount[ithread]){ + frameNumberInPreviousFile[ithread] = currentFrameNumber[ithread]; + totalPacketsInFile[ithread] = 0; + }else{ + frameNumberInPreviousFile[ithread] = -1; + frameNumberInPreviousCheck[ithread] = -1; + } + + + + return OK; +} + + + + +int UDPStandardImplementation::createCompressionFile(int ithread, int iframe){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + +#ifdef MYROOT1 + char temp[MAX_STR_LENGTH]; + //create file name for gui purposes, and set up acquistion parameters + sprintf(temp, "%s/%s_fxxx_%d_%d.root", filePath,fileNamePerThread[ithread],fileIndex,ithread); + //file + myFile[ithread] = new TFile(temp,"RECREATE");/** later return error if it exists */ + cprintf(GREEN,"Writing_Thread %d: Created Compression File: %s\n",ithread, temp); + //tree + sprintf(temp, "%s_fxxx_%d_%d",fileNamePerThread[ithread],fileIndex,ithread); + myTree[ithread]=singlePhotonDetectorObject[ithread]->initEventTree(temp, &iframe); + //resets the pedestalSubtraction array and the commonModeSubtraction + singlePhotonDetectorObject[ithread]->newDataSet(); + if(myFile[ithread]==NULL){ + FILE_LOG(logERROR) << "File Null"; + return FAIL; + } + if(!myFile[ithread]->IsOpen()){ + FILE_LOG(logERROR) << "File Not Open"; + return FAIL; + } + return OK; +#endif + return FAIL; +} + + + +void* UDPStandardImplementation::startDataCallbackThread(void* this_pointer){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + ((UDPStandardImplementation*)this_pointer)->startDataCallback(); + return this_pointer; +} + + + +void* UDPStandardImplementation::startListeningThread(void* this_pointer){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + ((UDPStandardImplementation*)this_pointer)->startListening(); + return this_pointer; +} + + + +void* UDPStandardImplementation::startWritingThread(void* this_pointer){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + ((UDPStandardImplementation*)this_pointer)->startWriting(); + return this_pointer; +} + + + + +void UDPStandardImplementation::startDataCallback(){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + //set current thread value index + int ithread = currentThreadIndex; + struct timespec begin,end; + + // server address to bind + char hostName[100] = "tcp://*:";//"tcp://127.0.0.1:"; + int portno = DEFAULT_ZMQ_PORTNO + (detID*numberofListeningThreads+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 + //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; + + + int headersize=0; + switch(myDetectorType){ + case EIGER: + headersize = EIGER_DATA_PACKET_HEADER_SIZE; break; + default: + 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) + while(true){ + + int oneframesize = oneDataSize * packetsPerFrame; + char* buffer = new char[packetsPerFrame*oneDataSize](); + memset(buffer,0xFF,oneframesize); + int size = 0; + int offset = 0; + uint32_t currentfnum = 0; + uint64_t fnum = 0; + uint32_t pnum = 0; + uint32_t snum = 0; + uint64_t bid = 0; + bool randomSendNow = true; + bool newFrame = false; + + + + //header details + const char *type = "float64"; + const char *shape= "[1024, 512]"; + const char *jsonFmt ="{\"htype\":[\"chunk-1.0\"], \"type\":\"%s\", \"shape\":%s, \"acqIndex\":%d, \"fIndex\":%d, \"subfnum\":%d, \"fname\":\"%s\"}"; + char buf[1000]; + int acquisitionIndex = -1; + int frameIndex = -1; + int subframeIndex = -1; +#ifdef DEBUG + int oldpnum = -1; +#endif + int datapacketscaught = 0; + + /* inner loop - loop for each buffer */ + //until mask reset (dummy pcaket got by writer) + while((1 << ithread) & dataCallbackThreadsMask){ + + //let the writer thread continue, while we process carry over if any + sem_post(&writerGuiSemaphore[ithread]); + //wait for receiver to send more data + sem_wait(&dataCallbackWriterSemaphore[ithread]); + + //end if acquistion + if(guiNumPackets[ithread] == dummyPacketValue){ + + //sending previous half frames if any + if(!excludeMissingPackets && newFrame){ + //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; + } + + + + //send final header + //update frame details +#ifdef DEBUG + cout << "sending dummy" << endl; +#endif + frameIndex = -9; + acquisitionIndex = -9; + subframeIndex = -9; + int len = sprintf(buf,jsonFmt,type,shape, acquisitionIndex, frameIndex, subframeIndex,completeFileName[ithread]); + zmq_send(zmqsocket, buf,len, ZMQ_SNDMORE); + //send final data + zmq_send (zmqsocket, "end", 3, 0); + + pthread_mutex_lock(&statusMutex); + dataCallbackThreadsMask^=(1<0) && (getFrameandPacketNumber(ithread, latestData[ithread]+offset, fnum, pnum,snum,bid)==FAIL)){ + offset+= onePacketSize; + } + //if(!ithread) cout<< ithread <<" fnum:"<< fnum<<" pnum:"<= size) + break; + + if(!frameToGuiFrequency) + currentfnum = fnum; + + + //last packet of same frame + if(fnum == currentfnum && pnum == (packetsPerFrame-1)){ +#ifdef DEBUG + oldpnum=0; +#endif + memcpy(buffer+(pnum*oneDataSize), latestData[ithread]+offset+headersize,oneDataSize); + offset+= onePacketSize; + //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 + 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); + + } + //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; + 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); + } + + memcpy(buffer+(pnum*oneDataSize), latestData[ithread]+offset+headersize,oneDataSize); + offset+= onePacketSize; + newFrame = true; + } + + } + } + + + + }/*--end of loop for each buffer (inner loop)*/ + + //free resources + delete[] buffer; + + //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){ + 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); +} + + + + +void UDPStandardImplementation::startListening(){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + //set current thread value index + int ithread = currentThreadIndex; + //let calling function know thread started and obtained current + threadStarted = 1; + + + uint32_t rc = 0; //size of buffer received in bytes + //split frames for data compression + int carryonBufferSize = 0; //from previous buffer to keep frames together in a buffer + char* tempBuffer = 0; //temporary buffer to store split frames + + + /* outer loop - loops once for each acquisition */ + //infinite loop, exited only to change dynamic range, 10G parameters etc (then recreated again) + while(true){ + + //compression variables reset before acquisition + if(dataCompressionEnable){ + carryonBufferSize = 0; + if(tempBuffer){delete []tempBuffer;tempBuffer=0;} + tempBuffer = new char[onePacketSize * (packetsPerFrame - 1)](); //store maximum of 1 packets less in a frame + } + + /* inner loop - loop for each buffer */ + //until mask reset (udp sockets shut down by client) + while((1 << ithread) & listeningThreadsMask){ + + + //pop from fifo + fifoFree[ithread]->pop(buffer[ithread]); + + //udpsocket doesnt exist + if(activated && !udpSocket[ithread]){ + FILE_LOG(logERROR) << "Listening_Thread " << ithread << ": UDP Socket not created or shut down earlier"; + stopListening(ithread,0); + continue; + } + + if(!activated) //eiger not activated modules + rc = prepareAndListenBufferDeactivated(ithread); + else if(excludeMissingPackets) //eiger and jungfrau + rc = prepareAndListenBufferCompleteFrames(ithread); + else{ + rc = prepareAndListenBuffer(ithread, carryonBufferSize, tempBuffer); //others + carryonBufferSize = 0; + } + + //problem in receiving or end of acquisition + if (status == TRANSMITTING||(rc <= 0 && activated == 0)){ + stopListening(ithread,rc); + continue; + } + + if(dataCompressionEnable) + (*((uint32_t*)(buffer[ithread]))) = processListeningBuffer(ithread, carryonBufferSize, tempBuffer, rc); + + //push buffer to FIFO + while(!fifo[ithread]->push(buffer[ithread])); + + }/*--end of loop for each buffer (inner loop)*/ + + //end of acquisition, wait for next acquisition/change of parameters + sem_wait(&listenSemaphore[ithread]); + + //check to exit thread (for change of parameters) - only EXIT possibility + if(killAllListeningThreads){ + cprintf(BLUE,"Listening_Thread %d:Goodbye!\n",ithread); + //free resources at exit + if(tempBuffer) delete[] tempBuffer; + pthread_exit(NULL); + } + + }/*--end of loop for each acquisition (outer loop) */ +} + + + + + +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] + fifoBufferHeaderSize, temp, cSize); + + //listen to after the carry over buffer + if(status != TRANSMITTING) + receivedSize = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + fifoBufferHeaderSize + cSize, + (bufferSize * numberofJobsPerBuffer) - cSize); + + if(receivedSize > 0){ + //write packet count to buffer + *((uint32_t*)(buffer[ithread])) = (receivedSize/onePacketSize); + totalListeningPacketCount[ithread] += (receivedSize/onePacketSize); + + //start indices for each start of scan/acquisition + if(!measurementStarted[ithread]) //and rc>0 + startFrameIndices(ithread); + } +#ifdef DEBUG + cprintf(BLUE, "Listening_Thread %d : Received bytes: %d. Expected bytes: %d\n", ithread, receivedSize, bufferSize * numberofJobsPerBuffer-cSize); +#endif + return receivedSize; +} + + + + + + + +int UDPStandardImplementation::prepareAndListenBufferDeactivated(int ithread){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + //last + if(currentFrameNumber[ithread] == numberOfFrames) + return 0; + + //copy dummy packets + memset(buffer[ithread] + fifoBufferHeaderSize, 0xFF,bufferSize); + + //write fnum and number of packets + (*((uint64_t*)(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS))) = currentFrameNumber[ithread]+1; + (*((uint32_t*)(buffer[ithread]))) = packetsPerFrame; + + //start indices for each start of scan/acquisition (rc > 0) + if(!measurementStarted[ithread]) + startFrameIndices(ithread); + + return bufferSize; +} + + + + +int UDPStandardImplementation::prepareAndListenBufferCompleteFrames(int ithread){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + int headerlength = 0; + uint32_t LASTPNUM = 0; + uint32_t FIRSTPNUM = 0; + int INCORDECR = 0; + switch(myDetectorType){ + case JUNGFRAU: + headerlength = JFRAU_HEADER_LENGTH; + FIRSTPNUM = packetsPerFrame-1; + LASTPNUM = 0; + INCORDECR = -1; + break; + case EIGER: + headerlength = EIGER_DATA_PACKET_HEADER_SIZE; + FIRSTPNUM = 0; + LASTPNUM = packetsPerFrame-1; + INCORDECR = 1; + break; + default:break; + } + + + int offset = fifoBufferHeaderSize; + uint32_t pnum = 0; + uint64_t fnum = 0; + uint64_t bnum = 0; + int rc = 0; + //from getframeandpacketnumber() + uint32_t pi = 0; + uint64_t fi = 0; + uint64_t bi = 0; + uint32_t si = 0; + + + //read first packet + pnum = FIRSTPNUM; //first packet number to validate + if(status != TRANSMITTING) rc = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + offset); + if(rc <= 0) return 0; + if(getFrameandPacketNumber(ithread,buffer[ithread] + offset,fi,pi,si,bi) == FAIL){ + pi = ALL_MASK_32; //got 0 from fpga + fi = ALL_MASK_32; + } + else + fnum = fi; //fnum of first packet + bnum = bi; //bnum of first packet + totalListeningPacketCount[ithread]++; +#ifdef VERBOSE + if(!ithread) cout << "1 pnum:" << pnum << endl; +#endif + //start indices for each start of scan/acquisition (rc > 0) + if(!measurementStarted[ithread]) + startFrameIndices(ithread); + + + while(true){ + + //------------------------------------------------------ correct packet -------------------------------------------------------- + if((myDetectorType == JUNGFRAU && pnum == pi) || //jungfrau only looks at pnum + (myDetectorType == EIGER && pnum == pi && fnum == fi)){ // eiger looks at pnum and fnum +#ifdef VERBOSE + if(!ithread) cout << "correct packet" << endl; +#endif + //copy only data + memcpy(buffer[ithread] + offset,buffer[ithread] + offset + headerlength, oneDataSize); + offset+=oneDataSize; + + //if complete frame + if(pnum == LASTPNUM) + break; + //else increment/decrement + pnum += INCORDECR; + + rc=0; //listen again + if(status != TRANSMITTING) + rc = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + offset); + if(rc <= 0){ //end: update ignored and return + if(myDetectorType == JUNGFRAU) + totalIgnoredPacketCount[ithread] += (packetsPerFrame - pnum); + else + totalIgnoredPacketCount[ithread] += (pnum + 1); + return 0; + } + totalListeningPacketCount[ithread]++; + if(getFrameandPacketNumber(ithread, buffer[ithread] + offset,fi,pi,si,bi) == FAIL){ + pi = ALL_MASK_32; //got 0 from fpga + fi = ALL_MASK_32; + totalIgnoredPacketCount[ithread] += (pnum + 1); + } + else if(myDetectorType == EIGER) + fnum = fi; //update currentfnum for eiger (next packets should have currentfnum value) +#ifdef VERBOSE + if(!ithread) cout << "next currentpnum :" << pnum << endl; +#endif + } + + //------------------------------------------------------ wrong packet -------------------------------------------------------- + else{ +#ifdef VERBOSE + if(!ithread) cprintf(RED,"wrong packet %d, expected packet %d fnum of last good one:%d\n", + pi,pnum,(*((uint32_t*)(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS)))); +#endif + if(myDetectorType == JUNGFRAU) + totalIgnoredPacketCount[ithread] += (packetsPerFrame - pnum -1); //extra 1 subtracted now to be added in the while loop anyway + else + totalIgnoredPacketCount[ithread] += pnum; //extra 1 subtracted now to be added in the while loop anyway + pnum = FIRSTPNUM; + offset = fifoBufferHeaderSize; + + //find the start of next image + while(pnum != pi){ + totalIgnoredPacketCount[ithread]++; + + rc=0; + if(status != TRANSMITTING) + rc = udpSocket[ithread]->ReceiveDataOnly(buffer[ithread] + offset); + if(rc <= 0){ + if(myDetectorType == JUNGFRAU) + totalIgnoredPacketCount[ithread] += (packetsPerFrame - pnum); + else + totalIgnoredPacketCount[ithread] += (pnum + 1); + return 0; + } + totalListeningPacketCount[ithread]++; + if(getFrameandPacketNumber(ithread, buffer[ithread] + offset,fi,pi,si,bi) == FAIL){ + pi = ALL_MASK_32; //got 0 from fpga + fi = ALL_MASK_32; + } +#ifdef VERBOSE + if(!ithread) cout << "trying to find pnum:" << pnum << " got " << pi << endl; +#endif + } + if(fi!=ALL_MASK_32) + fnum = fi; //fnum of first packet + bnum = bi; //bnum of first packet + } + } + //------------------------------------------------------ got a complete frame -------------------------------------------------------- + + //write frame number + (*((uint64_t*)(buffer[ithread]+HEADER_SIZE_NUM_TOT_PACKETS))) = fnum + startAcquisitionIndex; +#ifdef VERBOSE + if(!ithread) cout << "fnum:" << (*((uint64_t*)(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS))) << endl; +#endif + if(myDetectorType == JUNGFRAU) + (*((uint64_t*)(buffer[ithread] + HEADER_SIZE_NUM_TOT_PACKETS + FILE_HEADER_BUNCHID_OFFSET))) = bnum; + //write packet count to buffer + *((uint32_t*)(buffer[ithread])) = packetsPerFrame; + return bufferSize; +} + + + +void UDPStandardImplementation::startFrameIndices(int ithread){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + + jfrau_packet_header_t* header=0; + switch(myDetectorType){ + case EIGER: + startFrameIndex = 1; //frame number always resets + break; + case JUNGFRAU: + header = (jfrau_packet_header_t*)(buffer[ithread]+HEADER_SIZE_NUM_TOT_PACKETS); + startFrameIndex = (*( (uint32_t*) header->frameNumber))&frameIndexMask; + break; + default: + if(shortFrameEnable < 0){ + startFrameIndex = (((((uint32_t)(*((uint32_t*)(buffer[ithread] + fifoBufferHeaderSize))))+1) + & (frameIndexMask)) >> frameIndexOffset); + }else{ + startFrameIndex = ((((uint32_t)(*((uint32_t*)(buffer[ithread]+fifoBufferHeaderSize)))) + & (frameIndexMask)) >> frameIndexOffset); + } + break; + } + + //start of entire acquisition + if(!acqStarted){ + pthread_mutex_lock(&progressMutex); + 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); + measurementStarted[ithread] = true; +} + + + + + + + +void UDPStandardImplementation::stopListening(int ithread, int numbytes){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + +#ifdef DEBUG4 + cprintf(BLUE,"Listening_Thread %d: Stop Listening\nStatus: %s numbytes:%d\n", ithread, runStatusType(status).c_str(),numbytes); +#endif + + //free empty buffer + if(numbytes <= 0){ + FILE_LOG(logINFO) << "Listening "<< ithread << ": End of Acquisition"; + while(!fifoFree[ithread]->push(buffer[ithread])); + } + + + //push last non empty buffer into fifo + else{ + if(excludeMissingPackets){ + (*((uint32_t*)(buffer[ithread]))) = numbytes/oneDataSize; + totalListeningPacketCount[ithread] += (numbytes/oneDataSize); + }else{ + (*((uint32_t*)(buffer[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,(*((uint32_t*)(buffer[ithread]))) ); +#endif + while(!fifo[ithread]->push(buffer[ithread])); + } + + //push dummy-end buffer into fifo for all writer threads + fifoFree[ithread]->pop(buffer[ithread]); + + //creating dummy-end buffer with pc=0xFFFF + (*((uint32_t*)(buffer[ithread]))) = dummyPacketValue; + while(!fifo[ithread]->push(buffer[ithread])); + + + //reset mask and exit loop + pthread_mutex_lock(&statusMutex); + listeningThreadsMask^=(1<> frameIndexOffset)); +#endif + cSize = onePacketSize; + --packetCount; + } + } +#ifdef DEBUG4 + cprintf(BLUE, "Listening_Thread %d: First Header:%d\n", (((((uint32_t)(*((uint32_t*)(buffer[ithread] + fifoBufferHeaderSize))))+1) + & (frameIndexMask)) >> frameIndexOffset)); +#endif + break; + + case MOENCH: + lastPacketOffset = (((packetCount - 1) * onePacketSize) + fifoBufferHeaderSize); +#ifdef DEBUG4 + cprintf(BLUE, "Listening_Thread %d: First Header:%d\t First Packet:%d\t Last Header:%d\t Last Packet:%d\tLast Packet Offset:%d\n", + (((((uint32_t)(*((uint32_t*)(buffer[ithread]+fifoBufferHeaderSize))))) & (frameIndexMask)) >> frameIndexOffset), + ((((uint32_t)(*((uint32_t*)(buffer[ithread]+fifoBufferHeaderSize))))) & (packetIndexMask)), + (((((uint32_t)(*((uint32_t*)(buffer[ithread]+lastpacketoffset))))) & (frameIndexMask)) >> frameIndexOffset), + ((((uint32_t)(*((uint32_t*)(buffer[ithread]+lastpacketoffset))))) & (packetIndexMask)), + lastPacketOffset); +#endif + //moench last packet value is 0, so find the last packet and store the others in a temp storage + if( ((((uint32_t)(*((uint32_t*)(buffer[ithread]+lastPacketOffset))))) & (packetIndexMask))){ + lastFrameHeader = ((((uint32_t)(*((uint32_t*)(buffer[ithread]+lastPacketOffset))))) + & (frameIndexMask)) >> frameIndexOffset; + cSize += onePacketSize; + lastPacketOffset -= onePacketSize; + --packetCount; + while (lastFrameHeader == (((((uint32_t)(*((uint32_t*)(buffer[ithread]+lastPacketOffset))))) & (frameIndexMask)) >> frameIndexOffset)){ + cSize += onePacketSize; + lastPacketOffset -= onePacketSize; + --packetCount; + } + memcpy(temp, buffer[ithread]+(lastPacketOffset+onePacketSize), cSize); +#ifdef DEBUG4 + cprintf(BLUE, "Listening_Thread %d: temp Header:%d\t temp Packet:%d\n", + (((((uint32_t)(*((uint32_t*)(temp)))))& (frameIndexMask)) >> frameIndexOffset), + ((((uint32_t)(*((uint32_t*)(temp))))) & (packetIndexMask))); +#endif + } + break; + + default: + cprintf(RED,"Listening_Thread %d: Error: This detector %s is not implemented in the receiver\n", + ithread, getDetectorType(myDetectorType).c_str()); + break; + } + +#ifdef DEBUG4 + cprintf(BLUE,"Listening_Thread %d: PacketCount:%d CarryonBufferSize:%d\n",ithread, packetCount, cSize); +#endif + + return packetCount; +} + + + + + + +void UDPStandardImplementation::startWriting(){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + int ithread = currentThreadIndex; //set current thread value index + threadStarted = 1; //let calling function know thread started and obtained current + + char* wbuf = NULL; //buffer popped from FIFO + sfilefd[ithread] = 0; //file pointer + uint64_t nf = 0; //for compression, number of frames + + + /* outer loop - loops once for each acquisition */ + //infinite loop, exited only to change dynamic range, 10G parameters etc (then recreated again) + while(true){ + + //--reset parameters before acquisition (depending on compression) + nf = 0; //compression has only one listening thread (anything not eiger) + + + /* inner loop - loop for each buffer */ + //until mask unset (udp sockets shut down by client) + while((1 << ithread) & writerThreadsMask){ + + //pop + if(!dataCompressionEnable) + fifo[ithread]->pop(wbuf); + else + fifo[0]->pop(wbuf); + 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, dataCompressionEnable?0:ithread); +#endif + + + //end of acquisition + if(numPackets == dummyPacketValue){ +#ifdef DEBUG4 + cprintf(GREEN,"Writing_Thread %d: Dummy frame popped out of FIFO %d",ithread, dataCompressionEnable?0:ithread); +#endif + stopWriting(ithread,wbuf); + continue; + } + + //jungfrau and eiger + if(excludeMissingPackets) + handleCompleteFramesOnly(ithread, wbuf); + //normal + else if(!dataCompressionEnable) + handleWithoutDataCompression(ithread, wbuf, numPackets); + + //compression + else{ +#if defined(MYROOT1) && defined(ALLFILE_DEBUG) + if(npackets > 0) + writeFileWithoutCompression(wbuf, numPackets); +#endif + handleDataCompression(ithread,wbuf,nf); + } + }/*--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"; + + //in case they are not closed already + closeFile(ithread); +#ifdef DEBUG4 + cprintf(GREEN,"Writing_Thread %d: Done with acquisition. Waiting for 1st sem to create new file/change of parameters\n", ithread); +#endif + //end of acquisition, wait for file create/change of parameters + sem_wait(&writerSemaphore[ithread]); + //check to exit thread (for change of parameters) - only EXIT possibility + if(killAllWritingThreads){ + cprintf(GREEN,"Writing_Thread %d:Goodbye!\n",ithread); + pthread_exit(NULL); + } +#ifdef DEBUG4 + cprintf(GREEN,"Writing_Thread %d: Got 1st post. Creating File\n", ithread); +#endif + + + //pop fifo so that its empty + char* temp; + while(!fifo[ithread]->isEmpty()){ + cprintf(RED,"%d:fifo emptied\n", ithread); + fifo[ithread]->pop(temp); + fifoFree[ithread]->push(temp); + } + + //create file + if((1<push(wbuffer)); + + if(dataStreamEnable){ + sem_wait(&writerGuiSemaphore[ithread]); //ensure previous frame was processed + guiNumPackets[ithread] = dummyPacketValue; + sem_post(&dataCallbackWriterSemaphore[ithread]); //let it know its got data + } + + + //all threads need to close file, reset mask and exit loop + if(myDetectorType == EIGER && fileWriteEnable && (cbAction > DO_NOTHING)){ + updateFileHeader(ithread); + fseek(sfilefd[ithread],0,0); + fwrite((void*)fileHeader[ithread], 1, FILE_HEADER_SIZE, sfilefd[ithread]); + } + + //Print packet loss + //if(totalWritingPacketCountFromLastCheck[ithread]){ +#ifdef VERBOSE + if(numberofWriterThreads>1){ + printf("Thread:%d" + "\tLost:%lld" + "\t\tPackets:%lld" + "\tFrame#:%lld" + "\tPFrame#:%lld\n", + ithread, + ((frameNumberInPreviousCheck[ithread]+1+(maxFramesPerFile/progressFrequency))>numberOfFrames) + ?(long long int)((numberOfFrames-(frameNumberInPreviousCheck[ithread]+1))*packetsPerFrame - totalWritingPacketCountFromLastCheck[ithread]) + :(long long int)((frameNumberInPreviousCheck[ithread]+(maxFramesPerFile/progressFrequency) - frameNumberInPreviousCheck[ithread])*packetsPerFrame - totalWritingPacketCountFromLastCheck[ithread]), + (long long int)totalWritingPacketCountFromLastCheck[ithread], + (long long int)currentFrameNumber[ithread], + (long long int)frameNumberInPreviousCheck[ithread] + ); + }else{ + printf("Lost:%lld" + "\t\tPackets:%lld" + "\tFrame#:%lld" + "\tPFrame#:%lld\n", + ((frameNumberInPreviousCheck[ithread]+1+(maxFramesPerFile/progressFrequency))>numberOfFrames) + ?(long long int)((numberOfFrames-(frameNumberInPreviousCheck[ithread]+1))*packetsPerFrame - totalWritingPacketCountFromLastCheck[ithread]) + :(long long int)((frameNumberInPreviousCheck[ithread]+(maxFramesPerFile/progressFrequency) - frameNumberInPreviousCheck[ithread])*packetsPerFrame - totalWritingPacketCountFromLastCheck[ithread]), + (long long int)totalWritingPacketCountFromLastCheck[ithread], + (long long int)currentFrameNumber[ithread], + (long long int)frameNumberInPreviousCheck[ithread] + ); + } + + if(numberofWriterThreads>1){ + cprintf(BLUE,"File:%s" + "\nThread:%d" + "\tLost:%lld" + "\t\tPackets:%lld" + "\tFrame#:%lld" + "\tPFrame#:%lld\n", + completeFileName[ithread],ithread, + ((frameNumberInPreviousFile[ithread]+1+maxFramesPerFile)>numberOfFrames) + ?(long long int)((numberOfFrames-(frameNumberInPreviousFile[ithread]+1))*packetsPerFrame - totalPacketsInFile[ithread]) + :(long long int)((frameNumberInPreviousFile[ithread]+maxFramesPerFile - frameNumberInPreviousFile[ithread])*packetsPerFrame - totalPacketsInFile[ithread]), + (long long int)totalPacketsInFile[ithread], + (long long int)currentFrameNumber[ithread], + (long long int)frameNumberInPreviousFile[ithread] + ); + }else{ + cprintf(BLUE,"File:%s" + "\nLost:%lld" + "\t\tPackets:%lld" + "\tFrame#:%lld" + "\tPFrame#:%lld\n", + completeFileName[ithread], + ((frameNumberInPreviousFile[ithread]+1+maxFramesPerFile)>numberOfFrames) + ?(long long int)(numberOfFrames-(frameNumberInPreviousFile[ithread]+1)) + :(long long int)(frameNumberInPreviousFile[ithread]+maxFramesPerFile - frameNumberInPreviousFile[ithread]), + (long long int)totalPacketsInFile[ithread], + (long long int)currentFrameNumber[ithread], + (long long int)frameNumberInPreviousFile[ithread] + ); + } +#endif + //} + + closeFile(ithread); + pthread_mutex_lock(&statusMutex); + writerThreadsMask^=(1<push(wbuffer)); + + return; + } + + + //callback to write data + if (cbAction < DO_EVERYTHING) + rawDataReadyCallBack((int)tempframenumber, wbuffer + fifoBufferHeaderSize, npackets * onePacketSize, + sfilefd[ithread], latestData[ithread],pRawDataReady);//know which thread from sfilefd + + + //write to file if enabled and update write parameters + if(npackets > 0) + 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/numberofListeningThreads)) + if(dataStreamEnable && npackets > 0) + copyFrameToGui(ithread, wbuffer,npackets); +#ifdef DEBUG4 + cprintf(GREEN,"Writing_Thread: Copied frame\n"); +#endif + + + //free fifo addresses + int listenfifoThread = ithread; + if(dataCompressionEnable) + listenfifoThread = 0; + while(!fifoFree[listenfifoThread]->push(wbuffer)); +#ifdef EVERYFIFODEBUG + 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 %d \n",listenfifoThread, (void*)(wbuffer), listenfifoThread); +#endif + +} + + + + +void UDPStandardImplementation::handleCompleteFramesOnly(int ithread, char* wbuffer){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + //get current frame number + uint64_t tempframenumber; + tempframenumber = (*((uint64_t*)(wbuffer+HEADER_SIZE_NUM_TOT_PACKETS))); + tempframenumber -= startFrameIndex; + + + if (cbAction < DO_EVERYTHING) + rawDataReadyCallBack((int)tempframenumber, wbuffer + HEADER_SIZE_NUM_TOT_PACKETS, bufferSize + FILE_FRAME_HEADER_LENGTH, + sfilefd[ithread], latestData[ithread],pRawDataReady); + + + //write to file if enabled and update write parameters + if((fileWriteEnable) && (sfilefd[ithread])){ + if(tempframenumber && (tempframenumber%maxFramesPerFile) == 0) + createNewFile(ithread); + fwrite(wbuffer + HEADER_SIZE_NUM_TOT_PACKETS, 1, (bufferSize + FILE_FRAME_HEADER_LENGTH), sfilefd[ithread]); + } + + + //progress + if(tempframenumber && (tempframenumber%(maxFramesPerFile/progressFrequency)) == 0){ + if(numberofWriterThreads>1){ + printf("Thread:%d" + "\tLost:%lld" + "\t\tPackets:%lld" + "\tFrame#:%lld" + "\tPFrame#:%lld\n", + ithread, + ((frameNumberInPreviousCheck[ithread]+1+(maxFramesPerFile/progressFrequency))>numberOfFrames) + ?(long long int)((numberOfFrames-(frameNumberInPreviousCheck[ithread]+1))*packetsPerFrame - totalWritingPacketCountFromLastCheck[ithread]) + :(long long int)((frameNumberInPreviousCheck[ithread]+(maxFramesPerFile/progressFrequency) - frameNumberInPreviousCheck[ithread])*packetsPerFrame - totalWritingPacketCountFromLastCheck[ithread]), + (long long int)totalWritingPacketCountFromLastCheck[ithread], + (long long int)currentFrameNumber[ithread], + (long long int)frameNumberInPreviousCheck[ithread] + ); + }else{ + printf("Lost:%lld" + "\t\tPackets:%lld" + "\tFrame#:%lld" + "\tPFrame#:%lld\n", + ((frameNumberInPreviousCheck[ithread]+1+(maxFramesPerFile/progressFrequency))>numberOfFrames) + ?(long long int)((numberOfFrames-(frameNumberInPreviousCheck[ithread]+1))*packetsPerFrame - totalWritingPacketCountFromLastCheck[ithread]) + :(long long int)((frameNumberInPreviousCheck[ithread]+(maxFramesPerFile/progressFrequency) - frameNumberInPreviousCheck[ithread])*packetsPerFrame - totalWritingPacketCountFromLastCheck[ithread]), + (long long int)totalWritingPacketCountFromLastCheck[ithread], + (long long int)currentFrameNumber[ithread], + (long long int)frameNumberInPreviousCheck[ithread] + ); + } + //reset counters for each new file + frameNumberInPreviousCheck[ithread] = currentFrameNumber[ithread]; + totalWritingPacketCountFromLastCheck[ithread] = 0; + } + + totalWritingPacketCountFromLastCheck[ithread]+= packetsPerFrame; + totalPacketsInFile[ithread] += packetsPerFrame; + totalWritingPacketCount[ithread] += packetsPerFrame; + lastFrameNumberInFile[ithread] = tempframenumber; + currentFrameNumber[ithread] = tempframenumber; + //cout<<"curentframenumber:"< 1) + pthread_mutex_lock(&writeMutex); + + packetsCaught += packetsPerFrame; + totalPacketsCaught += packetsPerFrame; + if((currentFrameNumber[ithread] - startAcquisitionIndex) > acquisitionIndex) + acquisitionIndex = currentFrameNumber[ithread] - startAcquisitionIndex; + if((currentFrameNumber[ithread] - startFrameIndex) > frameIndex[ithread]) + frameIndex[ithread] = currentFrameNumber[ithread] - startFrameIndex; + + if(numberofWriterThreads > 1) + pthread_mutex_unlock(&writeMutex); + + if(!activated) + currentFrameNumber[ithread]++; + +#ifdef DEBUG4 + cprintf(GREEN,"Writing_Thread: Writing done\nGoing to copy frame\n"); +#endif + + + //copy frame for gui + if(dataStreamEnable) + copyFrameToGui(ithread, wbuffer, packetsPerFrame); +#ifdef DEBUG4 + cprintf(GREEN,"Writing_Thread: Copied frame\n"); +#endif + + + //free fifo addresses + while(!fifoFree[ithread]->push(wbuffer)); +#ifdef DEBUG5 + cprintf(GREEN,"Writing_Thread %d: Freed buffer, pushed into fifofree %p for listener %d \n",ithread, (void*)(wbuffer), ithread); +#endif + +} + + + +void UDPStandardImplementation::writeFileWithoutCompression(int ithread, char* wbuffer,uint32_t numpackets){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + //if write enabled + if((fileWriteEnable) && (sfilefd[ithread])){ + if(numpackets){ + int offset = fifoBufferHeaderSize; + 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 = 0; + uint32_t pnum = 0; + uint32_t snum = 0; + uint64_t bunchid = 0; + //if(ithread) cout<<"getting start frame number"<push(wbuffer)); + return; + } + //if(ithread) cout<<"done getting start frame number"<=0) &&(!((lastFrameNumberInFile[ithread]+1) % maxFramesPerFile))) + createNewFile(ithread); + + + //frames to save in one file + nextFileFrameNumber = (lastFrameNumberInFile[ithread]+1) + + (maxFramesPerFile - ((lastFrameNumberInFile[ithread]+1)%maxFramesPerFile)); + + if(writeUptoFrameNumber(ithread, wbuffer, offset, nextFileFrameNumber, numpackets, packetsWritten) == FAIL) + //weird frame number of zero from fpga + return; + + //update stats + numpackets -= packetsWritten; + totalPacketsInFile[ithread] += packetsWritten; + totalWritingPacketCount[ithread] += packetsWritten; + pthread_mutex_lock(&writeMutex); + packetsCaught += packetsWritten; + totalPacketsCaught += packetsWritten; + pthread_mutex_unlock(&writeMutex); + currentFrameNumber[ithread] = lastFrameNumberInFile[ithread]; + } + } + } + + //only update parameters + else{ + + if(numpackets){ + //get last frame number + uint64_t finalLastFrameNumberToSave = 0; + uint32_t pnum; + uint32_t snum; + uint64_t bunchid = 0; + if(getFrameandPacketNumber(ithread, wbuffer + fifoBufferHeaderSize + ((numpackets - 1) * onePacketSize), finalLastFrameNumberToSave,pnum,snum,bunchid) == FAIL){ + //error in frame number sent by fpga + while(!fifoFree[ithread]->push(wbuffer)); + return; + } + totalPacketsInFile[ithread] += numpackets; + totalWritingPacketCount[ithread] += numpackets; + lastFrameNumberInFile[ithread] = finalLastFrameNumberToSave; + currentFrameNumber[ithread] = finalLastFrameNumberToSave; + + } + + if(numberofWriterThreads > 1) pthread_mutex_lock(&writeMutex); + 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::updateFileHeader(int ithread){ + //update file header + time_t t = time(0); + sprintf(fileHeader[ithread], + "\nHeader\t\t: %d bytes\n" + "Top\t\t: %d\n" + "Left\t\t: %d\n" + "Active\t\t: %d\n" + "Frames Caught\t: %lld\n" + "Frames Lost\t: %lld\n" + "Dynamic Range\t: %d\n" + "Ten Giga\t: %d\n" + "Image Size\t: %d bytes\n" + "x\t\t: %d pixels\n" + "y\t\t: %d pixels\n" + "Total Frames\t: %lld\n" + "Exptime (ns)\t: %lld\n" + "Period (ns)\t: %lld\n" + "Timestamp\t: %s\n\n" + + "#Frame Header\n" + "Frame Number\t: 8 bytes\n" + "Bunch ID\t: 8 bytes\n", + FILE_HEADER_SIZE, + (flippedData[0]?0:1), + (ithread?0:1), + activated, + (long long int)(totalPacketsInFile[ithread]/packetsPerFrame), + ((frameNumberInPreviousFile[ithread]+1+maxFramesPerFile)>numberOfFrames) + ?(long long int)((numberOfFrames-(frameNumberInPreviousFile[ithread]+1)) - (totalPacketsInFile[ithread]/packetsPerFrame)) + :(long long int)((frameNumberInPreviousFile[ithread]+maxFramesPerFile - frameNumberInPreviousFile[ithread]) - (totalPacketsInFile[ithread]/packetsPerFrame)), + dynamicRange,tengigaEnable, + bufferSize, + //only for eiger right now + EIGER_PIXELS_IN_ONE_ROW,EIGER_PIXELS_IN_ONE_COL, + (long long int)numberOfFrames, + (long long int)acquisitionTime, + (long long int)acquisitionPeriod, + ctime(&t)); + if(strlen(fileHeader[ithread]) > FILE_HEADER_SIZE) + cprintf(BG_RED,"File Header Size %d is too small for fixed file header size %d\n",(int)strlen(fileHeader[ithread]),(int)FILE_HEADER_SIZE); + + +} + +//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{ + +#ifdef DEBUG4 + cprintf(GREEN,"Writing_Thread: CopyingFrame: Going to copy data\n"); +#endif + //ensure previous frame was processed + sem_wait(&writerGuiSemaphore[ithread]); + + //copy date + guiNumPackets[ithread] = numpackets; + strcpy(guiFileName[ithread],completeFileName[ithread]); + + if(excludeMissingPackets) //copy also the header + memcpy(latestData[ithread],buffer+HEADER_SIZE_NUM_TOT_PACKETS, bufferSize + FILE_FRAME_HEADER_LENGTH); + else //copy only the data + memcpy(latestData[ithread],buffer+ fifoBufferHeaderSize , numpackets*onePacketSize); + //let it know its got data + sem_post(&dataCallbackWriterSemaphore[ithread]); + +#ifdef DEBUG4 + cprintf(GREEN,"Writing_Thread: CopyingFrame: Copied Data\n"); +#endif + + } + + //update the counter for nth frame + if(frameToGuiFrequency) + frametoGuiCounter[ithread]++; + + +} + + + + + +void UDPStandardImplementation::handleDataCompression(int ithread, char* wbuffer, uint64_t &nf){ + FILE_LOG(logDEBUG) << __AT__ << " called"; + + //get frame number + uint64_t tempframenumber=-1; + uint32_t pnum; + uint32_t snum; + uint64_t bunchid=-1; + if(getFrameandPacketNumber(ithread, wbuffer + fifoBufferHeaderSize, tempframenumber,pnum,snum,bunchid) == 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+ fifoBufferHeaderSize; //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)); //number of total packets + int remainingsize = npackets * onePacketSize; //size of the memory slot to be analyzed + + eventType thisEvent = PEDESTAL; + int once = 0; + int xmax = 0, ymax = 0; //max pixels in x and y direction + int xmin = 1, ymin = 1; //min pixels in x and y direction + double tot, tl, tr, bl, br; + + //determining xmax and ymax + switch(myDetectorType){ + case MOENCH: + xmax = MOENCH_PIXELS_IN_ONE_ROW-1; + ymax = MOENCH_PIXELS_IN_ONE_ROW-1; + break; + case GOTTHARD: + if(shortFrameEnable == -1){ + xmax = GOTTHARD_PIXELS_IN_ROW-1; + ymax = GOTTHARD_PIXELS_IN_COL-1; + }else{ + xmax = GOTTHARD_SHORT_PIXELS_IN_ROW-1; + ymax = GOTTHARD_SHORT_PIXELS_IN_COL-1; + } + break; + default: + break; + } + + while(buff[0] = receiverData[ithread]->findNextFrame(data,ndata,remainingsize)){ + + //remaining number of packets + np = ndata/onePacketSize; + + if ((np == packetsPerFrame) && (buff[0]!=NULL)){ + if(nf == 1000) + cprintf(GREEN, "Writing_Thread %d: pedestal done\n", ithread); + + singlePhotonDetectorObject[ithread]->newFrame(); + + //only for moench + if(commonModeSubtractionEnable){ + for(int ix = xmin - 1; ix < xmax+1; ix++){ + for(int iy = ymin - 1; iy < ymax+1; iy++){ + thisEvent = singlePhotonDetectorObject[ithread]->getEventType(buff[0], ix, iy, 0); + } + } + } + + + for(int ix = xmin - 1; ix < xmax+1; ix++) + for(int iy = ymin - 1; iy < ymax+1; iy++){ + thisEvent=singlePhotonDetectorObject[ithread]->getEventType(buff[0], ix, iy, commonModeSubtractionEnable); + if (nf>1000) { + tot=0; + tl=0; + tr=0; + bl=0; + br=0; + if (thisEvent==PHOTON_MAX) { + receiverData[ithread]->getFrameNumber(buff[0]); + //iFrame=receiverData[ithread]->getFrameNumber(buff); +#ifdef MYROOT1 + myTree[ithread]->Fill(); + //cout << "Fill in event: frmNr: " << iFrame << " ix " << ix << " iy " << iy << " type " << thisEvent << endl; +#else + pthread_mutex_lock(&writeMutex); + if((fileWriteEnable) && (sfilefd[0])) + singlePhotonDetectorObject[ithread]->writeCluster(sfilefd[0]); + pthread_mutex_unlock(&writeMutex); +#endif + } + } + } + + nf++; + + +#ifndef ALLFILE + totalPacketsInFile[ithread] += (bufferSize/packetsPerFrame); + totalWritingPacketCount[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){ + if(dataStreamEnable) + copyFrameToGui(ithread, buff[0],(uint32_t)packetsPerFrame); + once = 1; + } + } + + remainingsize -= ((buff[0] + ndata) - data); + data = buff[0] + ndata; + if(data > (wbuffer + fifoBufferHeaderSize + npackets * onePacketSize) ) + cprintf(BG_RED,"Writing_Thread %d: Error: Compression data goes out of bounds!\n", ithread); + } + + + 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)); +#endif +#ifdef DEBUG5 + cprintf(GREEN,"Writing_Thread %d: Compression free pushed into fifofree %p for listerner 0\n", ithread, (void*)(wbuffer)); +#endif +} + + + +int UDPStandardImplementation::getFrameandPacketNumber(int ithread, char* wbuffer, uint64_t &framenumber, uint32_t &packetnumber,uint32_t &subframenumber, uint64_t &bunchid){ + 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; + bunchid = 0; + + switch(myDetectorType){ + + case EIGER: + footer = (eiger_packet_footer_t*)(wbuffer + footerOffset); + framenumber = (uint32_t)(*( (uint64_t*) footer)); + //error in frame number sent by fpga + if(((uint32_t)(*( (uint64_t*) footer)))==0){ + framenumber = 0; + FILE_LOG(logERROR) << "Fifo "<< ithread << ": Frame Number is zero from firmware."; + return FAIL; + } + packetnumber = (*( (uint16_t*) footer->packetNumber))-1; + 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 subfnum:%d footeroffset:%d\n", + ithread, + (long long int)framenumber, + packetnumber, + framenumber, + subframenumber, + footerOffset); +#endif + framenumber -= startFrameIndex; + break; + + case JUNGFRAU: + header = (jfrau_packet_header_t*)(wbuffer); + framenumber = (*( (uint32_t*) header->frameNumber))&frameIndexMask; + packetnumber = (uint32_t)(*( (uint8_t*) header->packetNumber)); + bunchid = (*((uint64_t*) header->bunchid)); +#ifdef DEBUG4 + cprintf(GREEN, "Writing_Thread %d: fnum:%lld\t pnum:%d bunchid:%lld\n", + (long long int)framenumber, + packetnumber, + (long long int)bunchid); +#endif + framenumber -= startFrameIndex; + break; + + default: + 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)) + framenumber++; + packetnumber = framenumber&packetIndexMask; + framenumber = (framenumber & frameIndexMask) >> frameIndexOffset; +#ifdef DEBUG4 + cprintf(GREEN, "Writing_Thread %d: fnum:%lld\t pnum:%d\n", + (long long int)framenumber, + packetnumber); +#endif + framenumber -= startFrameIndex; + 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"; + //if(ithread) cout<<"at writeUptoFrameNumber " << nextFrameNumber<< endl; + + + int startoffset = offset; + int endoffset = startoffset + numpackets * onePacketSize; + uint64_t tempframenumber=-1; + offset = endoffset; + uint32_t pnum; + uint32_t snum; + uint64_t bunchid=-1; + //get last frame number + if(getFrameandPacketNumber(ithread, wbuffer + (endoffset-onePacketSize), tempframenumber,pnum,snum,bunchid) == FAIL){ + //error in frame number sent by fpga + while(!fifoFree[ithread]->push(wbuffer)); + return FAIL; + } + //last packet's frame number < nextframenumber + if(tempframenumber=nextFrameNumber){ + offset -= bigIncrements; + if(offsetpush(wbuffer)); + return FAIL; + } + } + if(offsetpush(wbuffer)); + return FAIL; + } + } + while(tempframenumberpush(wbuffer)); + return FAIL; + } + } + + + fwrite(wbuffer + startoffset, 1, offset-startoffset, sfilefd[ithread]); + numPacketsWritten += ((offset-startoffset)/onePacketSize); + lastFrameNumberInFile[ithread] = (nextFrameNumber-1); + //if(ithread) cout<<"done with writeUptoFrameNumber" << endl; + return OK; +} + + + + + + + + diff --git a/slsReceiverSoftware/src/dummyMain.cpp b/slsReceiverSoftware/src/dummyMain.cpp new file mode 100644 index 000000000..3f4e9bb2d --- /dev/null +++ b/slsReceiverSoftware/src/dummyMain.cpp @@ -0,0 +1,48 @@ +/* A simple server in the internet domain using TCP + The port number is passed as an argument */ + +#include "sls_receiver_defs.h" +#include "dummyUDPInterface.h" +#include "slsReceiverTCPIPInterface.h" + +#include +#include + + +using namespace std; + + +int main(int argc, char *argv[]) { + int success; + int tcpip_port_no; + bool bottom = false; + cout << "CCCCCC" << endl; + dummyUDPInterface *udp=new dummyUDPInterface(); +// slsReceiverTCPIPInterface *tcpipInterface = new slsReceiverTCPIPInterface(success, udp, tcpip_port_no, bottom); + + + + +// if(tcpipInterface->start() == slsReceiverDefs::OK){ +// cout << "DONE!" << endl; +// string str; +// cin>>str; +// //wait and look for an exit keyword +// while(str.find("exit") == string::npos) +// cin>>str; +// //stop tcp server thread, stop udp socket +// tcpipInterface->stop(); +// } + +// if (tcpipInterface) +// delete tcpipInterface; + + udp->startReceiver(); + if(udp) + delete udp; + return 0; + + + +} + diff --git a/slsReceiverSoftware/src/main.cpp b/slsReceiverSoftware/src/main.cpp new file mode 100644 index 000000000..b4e5ef142 --- /dev/null +++ b/slsReceiverSoftware/src/main.cpp @@ -0,0 +1,121 @@ +/* A simple server in the internet domain using TCP + The port number is passed as an argument */ + +#include "sls_receiver_defs.h" +#include "slsReceiverUsers.h" + +#include +#include +#include //SIGINT +#include //system + +#include "utilities.h" +#include "logger.h" +using namespace std; + +slsReceiverUsers *receiver; + +void deleteReceiver(slsReceiverUsers* r){ + if(r){delete r;r=0;} +} + +void closeFile(int p){ + deleteReceiver(receiver); +} + +/* +int startAcquisitionCallBack(char* filePath, char* fileName, int fileIndex, int bufferSize, void* context) { + cout << "#### startAcquisitionCallBack ####" << endl; + cout << "* filePath: " << filePath << endl; + cout << "* fileName: " << fileName << endl; + cout << "* fileIndex: " << fileIndex << endl; + cout << "* bufferSize: " << bufferSize << endl; + return 1; +} + +void acquisitionFinishedCallBack(int totalFramesCaught, void* context) { + cout << "#### acquisitionFinishedCallBack ####" << endl; + cout << "* totalFramesCaught: " << totalFramesCaught << endl; +} + +void rawDataReadyCallBack(int currFrameNum, char* dataPointer, int dataSize, FILE* file, char* guiDataPointer, void* context) { + cout << "#### rawDataReadyCallBack ####" << endl; + cout << "* currFrameNum: " << currFrameNum << endl; + cout << "* dataSize: " << dataSize << endl; +} +*/ + +int main(int argc, char *argv[]) { + + //Catch signal SIGINT to close files properly + signal(SIGINT,closeFile); + + //system("setterm -linux term -background white -clear"); + + int ret = slsReceiverDefs::OK; + receiver = new slsReceiverUsers(argc, argv, ret); + + if(ret==slsReceiverDefs::FAIL){ + deleteReceiver(receiver); + return -1; + } + + + //register callbacks + + /** + callback arguments are + filepath + filename + fileindex + datasize + + return value is + 0 raw data ready callback takes care of open,close,write file + 1 callback writes file, we have to open, close it + 2 we open, close, write file, callback does not do anything + + registerCallBackStartAcquisition(int (*func)(char*, char*,int, int, void*),void *arg); + */ + //receiver->registerCallBackStartAcquisition(startAcquisitionCallBack,NULL); + + + /** + callback argument is + total farmes caught + registerCallBackAcquisitionFinished(void (*func)(int, void*),void *arg); + */ + //receiver->registerCallBackAcquisitionFinished(acquisitionFinishedCallBack,NULL); + + + /** + args to raw data ready callback are + framenum + datapointer + file descriptor + guidatapointer (NULL, no data required) + NEVER DELETE THE DATA POINTER + REMEMBER THAT THE CALLBACK IS BLOCKING + registerCallBackRawDataReady(void (*func)(int, char*, FILE*, char*, void*),void *arg); + */ + //receiver->registerCallBackRawDataReady(rawDataReadyCallBack,NULL); + + + + //start tcp server thread + if(receiver->start() == slsReceiverDefs::OK){ + FILE_LOG(logDEBUG1) << "DONE!" << endl; + string str; + cin>>str; + //wait and look for an exit keyword + while(str.find("exit") == string::npos) + cin>>str; + //stop tcp server thread, stop udp socket + receiver->stop(); + } + + deleteReceiver(receiver); + cout << "Goodbye!" << endl; + return 0; +} + diff --git a/slsReceiverSoftware/src/slsReceiver.cpp b/slsReceiverSoftware/src/slsReceiver.cpp new file mode 100644 index 000000000..04bc29563 --- /dev/null +++ b/slsReceiverSoftware/src/slsReceiver.cpp @@ -0,0 +1,190 @@ +/********************************************//** + * @file slsReceiver.cpp + * @short creates the UDP and TCP class objects + ***********************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "slsReceiver.h" +//#include "UDPInterface.h" + +using namespace std; + + + +slsReceiver::slsReceiver(int argc, char *argv[], int &success){ + + /** + * Constructor method to start up a Receiver server. Reads configuration file, options, and + * assembles a Receiver using TCP and UDP detector interfaces + * + * @param iarg + * + * @return + */ + + udp_interface = NULL; + tcpipInterface = NULL; + + //creating base receiver + map configuration_map; + int tcpip_port_no = 1954; + success=OK; + string fname = ""; + string udp_interface_type = "standard"; + string rest_hostname = "localhost:8081"; + udp_interface = NULL; + + //parse command line for config + static struct option long_options[] = { + /* These options set a flag. */ + //{"verbose", no_argument, &verbose_flag, 1}, + /* These options don’t set a flag. + We distinguish them by their indices. */ + {"type", required_argument, 0, 't'}, + {"config", required_argument, 0, 'f'}, + {"rx_tcpport", required_argument, 0, 'b'}, + {"rest_hostname", required_argument, 0, 'r'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + /* getopt_long stores the option index here. */ + int option_index = 0; + int c=0; + optind = 1; + + while ( c != -1 ){ + c = getopt_long (argc, argv, "bfhtr", long_options, &option_index); + + /* Detect the end of the options. */ + if (c == -1) + break; + + switch(c){ + + case 'f': + fname = optarg; + //cout << long_options[option_index].name << " " << optarg << endl; + break; + + case 'b': + sscanf(optarg, "%d", &tcpip_port_no); + break; + + case 't': + udp_interface_type = optarg; + break; + + case 'r': + rest_hostname = optarg; + break; + + case 'h': + string help_message = """\nSLS Receiver Server\n\n"""; + help_message += """usage: slsReceiver --config config_fname [--rx_tcpport port]\n\n"""; + help_message += """\t--config:\t configuration filename for SLS Detector receiver\n"""; + help_message += """\t--rx_tcpport:\t TCP Communication Port with the client. Default: 1954.\n\n"""; + help_message += """\t--rest_hostname:\t Receiver hostname:port. It applies only to REST receivers, and indicates the hostname of the REST backend. Default: localhost:8081.\n\n"""; + + help_message += """\t--type:\t Type of the receiver. Possible arguments are: standard, REST. Default: standard.\n\n"""; + + cout << help_message << endl; + break; + + } + } + + // if required fname parameter not available, fail + //if (fname == "") + // success = FAIL; + + if( !fname.empty() ){ + try{ + FILE_LOG(logINFO) << "config file name " << fname; + success = read_config_file(fname, &tcpip_port_no, &configuration_map); + //VERBOSE_PRINT("Read configuration file of " + iline + " lines"); + } + catch(...){ + FILE_LOG(logERROR) << "Error opening configuration file " << fname ; + success = FAIL; + } + } + + + if(success != OK){ + FILE_LOG(logERROR) << "Failed: see output above for more information " ; + } + + if (success==OK){ + FILE_LOG(logINFO) << "SLS Receiver starting " << udp_interface_type << " on port " << tcpip_port_no << endl; +#ifdef REST + udp_interface = UDPInterface::create(udp_interface_type); + udp_interface->configure(configuration_map); +#endif + tcpipInterface = new slsReceiverTCPIPInterface(success, udp_interface, tcpip_port_no); + } +} + + +slsReceiver::~slsReceiver() { + if(udp_interface) + delete udp_interface; + if(tcpipInterface) + delete tcpipInterface; +} + + +int slsReceiver::start() { + return tcpipInterface->start(); +} + + +void slsReceiver::stop() { + tcpipInterface->stop(); +} + + +void slsReceiver::closeFile(int p) { + tcpipInterface->closeFile(p); +} + + +int64_t slsReceiver::getReceiverVersion(){ + return tcpipInterface->getReceiverVersion(); +} + + +void slsReceiver::registerCallBackStartAcquisition(int (*func)(char*, char*,int, int, void*),void *arg){ + //tcpipInterface + if(udp_interface) + udp_interface->registerCallBackStartAcquisition(func,arg); + else + tcpipInterface->registerCallBackStartAcquisition(func,arg); +} + + + +void slsReceiver::registerCallBackAcquisitionFinished(void (*func)(int, void*),void *arg){ + //tcpipInterface + if(udp_interface) + udp_interface->registerCallBackAcquisitionFinished(func,arg); + else + tcpipInterface->registerCallBackAcquisitionFinished(func,arg); +} + + +void slsReceiver::registerCallBackRawDataReady(void (*func)(int, char*, int, FILE*, char*, void*),void *arg){ + //tcpipInterface + if(udp_interface) + udp_interface->registerCallBackRawDataReady(func,arg); + else + tcpipInterface->registerCallBackRawDataReady(func,arg); +} + + diff --git a/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp new file mode 100644 index 000000000..e477c6655 --- /dev/null +++ b/slsReceiverSoftware/src/slsReceiverTCPIPInterface.cpp @@ -0,0 +1,3414 @@ +/********************************************//** + * @file slsReceiverTCPIPInterface.cpp + * @short interface between receiver and client + ***********************************************/ + +#include "slsReceiverTCPIPInterface.h" +#include "UDPInterface.h" +#include "gitInfoReceiver.h" +#include "slsReceiverUsers.h" +#include "slsReceiver.h" + +#include //EXIT +#include +#include +#include +#include +#include +#include //linux5 +#define be64toh(x) __bswap_64 (x) //linux5 +//#include //linux6 +using namespace std; + + + +slsReceiverTCPIPInterface::~slsReceiverTCPIPInterface() { + stop(); + if(mySock) {delete mySock; mySock=NULL;} +} + +slsReceiverTCPIPInterface::slsReceiverTCPIPInterface(int &success, UDPInterface* rbase, int pn): + myDetectorType(GOTTHARD), + receiverBase(rbase), + ret(OK), + lockStatus(0), + shortFrame(-1), + packetsPerFrame(GOTTHARD_PACKETS_PER_FRAME), + dynamicrange(16), + killTCPServerThread(0), + tenGigaEnable(0), + portNumber(DEFAULT_PORTNO+2), + mySock(NULL){ + + strcpy(SET_RECEIVER_ERR_MESSAGE,"Receiver not set up. Please use rx_hostname first.\n"); + + //***callback parameters*** + startAcquisitionCallBack = NULL; + pStartAcquisition = NULL; + acquisitionFinishedCallBack = NULL; + pAcquisitionFinished = NULL; + rawDataReadyCallBack = NULL; + pRawDataReady = NULL; + + unsigned short int port_no=portNumber; + if(receiverBase == NULL) receiverBase = 0; + + if (pn>0) + port_no = pn; + + success=OK; + + //create socket + if(success == OK){ + mySock = new MySocketTCP(port_no); + if (mySock->getErrorStatus()) { + success = FAIL; + delete mySock; + mySock=NULL; + } else { + portNumber=port_no; + //initialize variables + strcpy(mySock->lastClientIP,"none"); + strcpy(mySock->thisClientIP,"none1"); + strcpy(mess,"dummy message"); + function_table(); +#ifdef VERYVERBOSE + cout << "Function table assigned." << endl; +#endif + } + } + +} + + +int slsReceiverTCPIPInterface::setPortNumber(int pn){ + int p_number; + + MySocketTCP *oldsocket=NULL;; + int sd=0; + + if (pn>0) { + p_number = pn; + + if (p_number<1024) { + sprintf(mess,"Too low port number %d\n", p_number); + cout << mess << endl; + } else { + + oldsocket=mySock; + mySock = new MySocketTCP(p_number); + if(mySock){ + sd = mySock->getErrorStatus(); + if (!sd){ + portNumber=p_number; + strcpy(mySock->lastClientIP,oldsocket->lastClientIP); + delete oldsocket; + } else { + cout << "Could not bind port " << p_number << endl; + if (sd==-10) { + + cout << "Port "<< p_number << " already set" << endl; + } else { + delete mySock; + mySock=oldsocket; + } + } + + } else { + mySock=oldsocket; + } + } + } + + return portNumber; +} + + + +int slsReceiverTCPIPInterface::start(){ + FILE_LOG(logDEBUG) << "Creating TCP Server Thread" << endl; + killTCPServerThread = 0; + if(pthread_create(&TCPServer_thread, NULL,startTCPServerThread, (void*) this)){ + cout << "Could not create TCP Server thread" << endl; + return FAIL; + } + //#ifdef VERYVERBOSE + FILE_LOG(logDEBUG) << "TCP Server thread created successfully." << endl; + //#endif + return OK; +} + + +void slsReceiverTCPIPInterface::stop(){ + cout << "Shutting down UDP Socket" << endl; + killTCPServerThread = 1; + if(mySock) mySock->ShutDownSocket(); + cout<<"Socket closed"<startTCPServer(); + return this_pointer; +} + + +void slsReceiverTCPIPInterface::startTCPServer(){ + + +#ifdef VERYVERBOSE + cout << "Starting Receiver TCP Server" << endl; +#endif + int v=OK; + + while(1) { +#ifdef VERYVERBOSE + cout<< endl; +#endif +#ifdef VERY_VERBOSE + cout << "Waiting for client call" << endl; +#endif + if(mySock->Connect()>=0){ +#ifdef VERY_VERBOSE + cout << "Conenction accepted" << endl; +#endif + v = decode_function(); +#ifdef VERY_VERBOSE + cout << "function executed" << endl; +#endif + mySock->Disconnect(); +#ifdef VERY_VERBOSE + cout << "connection closed" << endl; +#endif + } + + //if tcp command was to exit server + if(v==GOODBYE){ + cout << "Shutting down UDP Socket" << endl; + if(receiverBase){ + receiverBase->shutDownUDPSockets(); + + cout << "Closing Files... " << endl; + receiverBase->closeFile(); + } + + mySock->exitServer(); + pthread_exit(NULL); + } + + //if user entered exit + if(killTCPServerThread) + pthread_exit(NULL); + + } +} + + + + + +int slsReceiverTCPIPInterface::function_table(){ + + for (int i=0;iReceiveDataOnly(&fnum,sizeof(fnum)); + if (n <= 0) { +#ifdef VERYVERBOSE + cout << "ERROR reading from socket " << n << ", " << fnum << endl; +#endif + return FAIL; + } +#ifdef VERYVERBOSE + else + cout << "size of data received " << n <numberOfFunctions-1) + fnum = numberOfFunctions-1; + //calling function + (this->*flist[fnum])(); + if (ret==FAIL) + cprintf(RED, "Error executing the function = %d\n",fnum); + + return ret; +} + + + + + + +int slsReceiverTCPIPInterface::M_nofunc(){ + + ret=FAIL; + sprintf(mess,"Unrecognized Function\n"); + cout << mess << endl; + + mySock->SendDataOnly(&ret,sizeof(ret)); + mySock->SendDataOnly(mess,sizeof(mess)); + + return GOODBYE; +} + + + + +void slsReceiverTCPIPInterface::closeFile(int p){ + receiverBase->closeFile(); +} + + +int slsReceiverTCPIPInterface::set_detector_type(){ + ret=OK; + detectorType retval=GENERIC; + detectorType dr; + strcpy(mess,"Could not set detector type range\n"); + + + // receive arguments + if(mySock->ReceiveDataOnly(&dr,sizeof(dr)) < 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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if((receiverBase)&&(receiverBase->getStatus()!= IDLE)){ + strcpy(mess,"Can not set detector type while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + + switch(dr){ + case GOTTHARD: + case PROPIX: + case MOENCH: + case EIGER: + case JUNGFRAUCTB: + case JUNGFRAU: + break; + default: + sprintf(mess,"Unknown detector type: %d\n", dr); + ret = FAIL; + break; + } + if(ret != FAIL){ +#ifndef REST + if(receiverBase == NULL){ + receiverBase = UDPInterface::create("standard"); + if(startAcquisitionCallBack) + receiverBase->registerCallBackStartAcquisition(startAcquisitionCallBack,pStartAcquisition); + if(acquisitionFinishedCallBack) + receiverBase->registerCallBackAcquisitionFinished(acquisitionFinishedCallBack,pAcquisitionFinished); + if(rawDataReadyCallBack) + receiverBase->registerCallBackRawDataReady(rawDataReadyCallBack,pRawDataReady); + } +#endif + myDetectorType = dr; + ret=receiverBase->setDetectorType(myDetectorType); + retval = myDetectorType; + } + + } + } + //#ifdef VERYVERBOSE + if(ret!=FAIL) + FILE_LOG(logDEBUG) << "detector type " << dr; + else + cprintf(RED, "%s\n", mess); + //#endif +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL) + mySock->SendDataOnly(mess,sizeof(mess)); + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + + + +int slsReceiverTCPIPInterface::set_file_name() { + ret=OK; + char* retval = NULL; + char defaultVal[MAX_STR_LENGTH] = ""; + char fName[MAX_STR_LENGTH]; + strcpy(mess,"Could not set file name"); + + // receive arguments + if(mySock->ReceiveDataOnly(fName,MAX_STR_LENGTH) < 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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set file name while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + receiverBase->setFileName(fName); + retval = receiverBase->getFileName(); + if(retval == NULL) + ret = FAIL; + } + } +#ifdef VERYVERBOSE + if(ret!=FAIL) + cout << "file name:" << retval << endl; + else + cout << mess << endl; +#endif +#endif + + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + if(retval == NULL) + mySock->SendDataOnly(defaultVal,MAX_STR_LENGTH); + else{ + mySock->SendDataOnly(retval,MAX_STR_LENGTH); + delete[] retval; + } + + + //return ok/fail + return ret; +} + + + + + + +int slsReceiverTCPIPInterface::set_file_dir() { + ret=OK; + char* retval=NULL; + char defaultVal[MAX_STR_LENGTH] = ""; + char fPath[MAX_STR_LENGTH]; + strcpy(mess,"Could not set file path\n"); + + // receive arguments + if(mySock->ReceiveDataOnly(fPath,MAX_STR_LENGTH) < 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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set file path while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + receiverBase->setFilePath(fPath); + retval = receiverBase->getFilePath(); + if(retval == NULL){ + ret = FAIL; + strcpy(mess,"receiver file path does not exist\n"); + } + } + + } +#ifdef VERYVERBOSE + if(ret!=FAIL) + cout << "file path:" << retval << endl; + else + cout << mess << endl; +#endif +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + if(retval == NULL) + mySock->SendDataOnly(defaultVal,MAX_STR_LENGTH); + else{ + mySock->SendDataOnly(retval,MAX_STR_LENGTH); + delete[] retval; + } + + //return ok/fail + return ret; +} + + + + + +// LEO: do we need it in the base class? +int slsReceiverTCPIPInterface::set_file_index() { + ret=OK; + int retval=-1; + int index; + strcpy(mess,"Could not set file index\n"); + + + // receive arguments + if(mySock->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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set file index while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + if(index >= 0) + receiverBase->setFileIndex(index); + retval=receiverBase->getFileIndex(); + if(index>=0 && retval!=index) + ret = FAIL; + } + } +#ifdef VERYVERBOSE + if(ret!=FAIL) + cout << "file index:" << retval << endl; + else + cout << mess << endl; +#endif +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + + + + +int slsReceiverTCPIPInterface::set_frame_index() { + ret=OK; + int retval=-1; + int index; + strcpy(mess,"Could not set frame index\n"); + + + // receive arguments + if(mySock->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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set frame index while receiver not idle\n"); + cprintf(RED,"%s",mess); + 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, + //so implement the interface here + + switch(index){ + case -1: index=0; break; + default: index=1; break; //value is 0 + } + receiverBase->setFrameIndexEnable(index); + retval=receiverBase->getFrameIndexEnable(); + switch(retval){ + case 0: retval=-1; break; + case 1: retval=0; break; + } + } + } +#ifdef VERYVERBOSE + if(ret!=FAIL) + cout << "frame index:" << retval << endl; + else + cout << mess << endl; +#endif +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + + +//LEO: is the client that commands the setup, or you just need the args? +int slsReceiverTCPIPInterface::setup_udp(){ + ret=OK; + strcpy(mess,"could not set up udp connection"); + char retval[MAX_STR_LENGTH] = ""; + char args[3][MAX_STR_LENGTH]; + + string temp; + int udpport,udpport2; + char eth[MAX_STR_LENGTH]; + + + // receive arguments + + if(mySock->ReceiveDataOnly(args,sizeof(args)) < 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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set up udp while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + //set up udp port + sscanf(args[1],"%d",&udpport); + sscanf(args[2],"%d",&udpport2); + receiverBase->setUDPPortNumber(udpport); + receiverBase->setUDPPortNumber2(udpport2); + //setup udpip + //get ethernet interface or IP to listen to + FILE_LOG(logINFO) << "Receiver UDP IP: " << args[0]; + temp = genericSocket::ipToName(args[0]); + if(temp=="none"){ + ret = FAIL; + strcpy(mess, "Failed to get ethernet interface or IP\n"); + FILE_LOG(logERROR) << mess; + } + else{ + strcpy(eth,temp.c_str()); + if (strchr(eth,'.')!=NULL) { + strcpy(eth,""); + ret = FAIL; + } + receiverBase->setEthernetInterface(eth); + + //get mac address from ethernet interface + if (ret != FAIL) + temp = genericSocket::nameToMac(eth); + + + if ((temp=="00:00:00:00:00:00") || (ret == FAIL)){ + ret = FAIL; + strcpy(mess,"failed to get mac adddress to listen to\n"); + } + else{ + strcpy(retval,temp.c_str()); + FILE_LOG(logINFO) << "Reciever MAC Address: " << retval; + } + } + } + } +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + FILE_LOG(logERROR) << mess; + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(retval,MAX_STR_LENGTH); + + //return ok/fail + return ret; +} + + + + + + +int slsReceiverTCPIPInterface::start_receiver(){ + ret=OK; + ret=OK; + enum runStatus s = ERROR; + strcpy(mess,"Could not start receiver\n"); + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + if (lockStatus==1 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + /* + else if(!strlen(receiverBase->getFilePath())){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE"); + ret = FAIL; + } + */ + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else { + s = receiverBase->getStatus(); + if(s == IDLE) + ret=receiverBase->startReceiver(mess); + else{ + sprintf(mess,"Cannot start Receiver as it is in %s state\n",runStatusType(s).c_str()); + cprintf(RED,"%s",mess); + ret=FAIL; + } + } +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "Error:%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + //return ok/fail + return ret; + + +} + + +int slsReceiverTCPIPInterface::stop_receiver(){ + ret=OK; + enum runStatus s = ERROR; + strcpy(mess,"Could not stop receiver\n"); + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + if (lockStatus==1 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else{ + if(receiverBase->getStatus()!= IDLE){ + receiverBase->stopReceiver(); + } + s = receiverBase->getStatus(); + if(s==IDLE) + ret = OK; + else{ + sprintf(mess,"Could not stop receiver. It is in %s state\n",runStatusType(s).c_str()); + cprintf(RED,"%s",mess); + ret = FAIL; + } + } +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + //return ok/fail + return ret; + + +} + + +int slsReceiverTCPIPInterface::get_status(){ + ret=OK; + int retval=-1; + enum runStatus s=ERROR; + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + }else s=receiverBase->getStatus(); +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + retval = (runStatus(s)); + mySock->SendDataOnly(&retval,sizeof(retval)); + //return ok/fail + return ret; + + +} + + +int slsReceiverTCPIPInterface::get_frames_caught(){ + ret=OK; + int retval=-1; + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + }else retval=receiverBase->getTotalFramesCaught(); +#endif + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + //return ok/fail + return ret; + + +} + + +int slsReceiverTCPIPInterface::get_frame_index(){ + ret=OK; + int retval=-1; + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + }else + retval=receiverBase->getAcquisitionIndex(); +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + //return ok/fail + return ret; + + +} + + +int slsReceiverTCPIPInterface::reset_frames_caught(){ + ret=OK; + + strcpy(mess,"Could not reset frames caught\n"); + + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + if (ret==OK) { + if (lockStatus==1 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + 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(); + } +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + //return ok/fail + return ret; + + +} + + + + + + +int slsReceiverTCPIPInterface::set_short_frame() { + ret=OK; + int index=0; + int retval=-100; + strcpy(mess,"Could not set/reset short frame for receiver\n"); + + //does not exist for moench + if(myDetectorType==MOENCH){ + strcpy(mess,"can not set short frame for moench\n"); + ret = FAIL; + } + + // receive arguments + if(mySock->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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Cannot set short frame while status is running\n"); + cprintf(RED,"%s",mess); + ret=FAIL; + } + else{ + receiverBase->setShortFrameEnable(index); + retval = receiverBase->getShortFrameEnable(); + shortFrame = retval; + if(shortFrame==-1) + packetsPerFrame=GOTTHARD_PACKETS_PER_FRAME; + else + packetsPerFrame=GOTTHARD_SHORT_PACKETS_PER_FRAME; + } + } +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + +int slsReceiverTCPIPInterface::read_frame(){ + switch(myDetectorType){ + case MOENCH: + return moench_read_frame(); + case EIGER: + return eiger_read_frame(); + case PROPIX: + return propix_read_frame(); + case JUNGFRAU: + return jungfrau_read_frame(); + default: + return gotthard_read_frame(); + } +} + + + +int slsReceiverTCPIPInterface::moench_read_frame(){ + ret=OK; + char fName[MAX_STR_LENGTH]=""; + int acquisitionIndex = -1; + int frameIndex= -1; + int i; + + + int bufferSize = MOENCH_BUFFER_SIZE; + int rnel = bufferSize/(sizeof(int)); + int* retval = new int[rnel]; + int* origVal = new int[rnel]; + //all initialized to 0 + for(i=0;igetFramesCaught()){ + startAcquisitionIndex = -1; + cout<<"haven't caught any frame yet"<readFrame(0,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"<> MOENCH_FRAME_INDEX_OFFSET); + + uint32_t numPackets = MOENCH_PACKETS_PER_FRAME; //40 + uint32_t onePacketSize = MOENCH_DATA_BYTES / MOENCH_PACKETS_PER_FRAME; //1280*40 / 40 = 1280 + uint32_t packetDatabytes_row = onePacketSize * (MOENCH_BYTES_IN_ONE_ROW / MOENCH_BYTES_PER_ADC); //1280 * 4 = 5120 + uint32_t partsPerFrame = onePacketSize / MOENCH_BYTES_PER_ADC; // 1280 / 80 = 16 + uint32_t packetOffset = 0; + int packetIndex,x,y; + int iPacket = 0; + offset = 4; + + + while (iPacket < (int)numPackets){ +#ifdef VERYVERBOSE + printf("iPacket:%d\n",iPacket);cout << endl; +#endif + //if missing packets, dont send to gui + bindex = (*((uint32_t*)(((char*)origVal)+packetOffset))); + if (bindex == 0xFFFFFFFF){ + cout << "Missing Packet,Not sending to gui" << endl; + index = startAcquisitionIndex - 1; + break;//use continue and change index above if you want to display missing packets with 0 value anyway in gui + } + + packetIndex = bindex & MOENCH_PACKET_INDEX_MASK; + //cout<<"packetIndex:"<= 40) && (packetIndex < 0)) + cout << "cannot decode packet index:" << packetIndex << endl; + else{ + + x = packetIndex / 10; + y = packetIndex % 10; +#ifdef VERYVERBOSE + cout<<"x:"<differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + else{ + mySock->SendDataOnly(fName,MAX_STR_LENGTH); + mySock->SendDataOnly(&acquisitionIndex,sizeof(acquisitionIndex)); + mySock->SendDataOnly(&frameIndex,sizeof(frameIndex)); + mySock->SendDataOnly(retval,MOENCH_DATA_BYTES); + } + //return ok/fail + + + delete [] retval; + delete [] origVal; + delete [] raw; + + return ret; + +} + + + + +int slsReceiverTCPIPInterface::gotthard_read_frame(){ + ret=OK; + char fName[MAX_STR_LENGTH]=""; + int acquisitionIndex = -1; + int frameIndex= -1; + int i; + + + //retval is a full frame + int bufferSize = GOTTHARD_BUFFER_SIZE; + int rnel = bufferSize/(sizeof(int)); + int* retval = new int[rnel]; + int* origVal = new int[rnel]; + //all initialized to 0 + for(i=0;igetFramesCaught()){ + startAcquisitionIndex=-1; + cout<<"haven't caught any frame yet"<readFrame(0,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"<> GOTTHARD_SHORT_FRAME_INDEX_OFFSET); +#ifdef VERYVERBOSE + cout << "index:" << hex << index << endl; +#endif + }else{ + bindex = ((uint32_t)(*((uint32_t*)raw)))+1; + pindex = (bindex & GOTTHARD_PACKET_INDEX_MASK); + index = ((bindex & GOTTHARD_FRAME_INDEX_MASK) >> GOTTHARD_FRAME_INDEX_OFFSET); + bindex2 = ((uint32_t)(*((uint32_t*)((char*)(raw+onebuffersize)))))+1; + pindex2 =(bindex2 & GOTTHARD_PACKET_INDEX_MASK); + index2 =((bindex2 & GOTTHARD_FRAME_INDEX_MASK) >> GOTTHARD_FRAME_INDEX_OFFSET); +#ifdef VERYVERBOSE + cout << "index1:" << hex << index << endl; + cout << "index2:" << hex << index << endl; +#endif + } + + memcpy(origVal,raw + HEADER_SIZE_NUM_TOT_PACKETS,bufferSize); + raw=NULL; + + + //1 adc + if(shortFrame!=-1){ + if(bindex != 0xFFFFFFFF) + memcpy((((char*)retval)+(GOTTHARD_SHORT_DATABYTES*shortFrame)),((char*) origVal)+4, GOTTHARD_SHORT_DATABYTES); + else{ + index = startAcquisitionIndex - 1; + cout << "Missing Packet,Not sending to gui" << endl; + } + } + //all adc + else{ + /*//ignore if half frame is missing + if ((bindex != 0xFFFFFFFF) && (bindex2 != 0xFFFFFFFF)){*/ + + //should be same frame + if (index == index2){ + //ideal situation (should be odd, even(index+1)) + if(!pindex){ + memcpy(retval,((char*) origVal)+4, onedatasize); + memcpy((((char*)retval)+onedatasize), ((char*) origVal)+10+onedatasize, onedatasize); + } + //swap to even,odd + else{ + memcpy((((char*)retval)+onedatasize),((char*) origVal)+4, onedatasize); + memcpy(retval, ((char*) origVal)+10+onedatasize, onedatasize); + index=index2; + } + }else + cout << "different frames caught. frame1:"<< hex << index << ":"<differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + else{ + mySock->SendDataOnly(fName,MAX_STR_LENGTH); + mySock->SendDataOnly(&acquisitionIndex,sizeof(acquisitionIndex)); + mySock->SendDataOnly(&frameIndex,sizeof(frameIndex)); + mySock->SendDataOnly(retval,GOTTHARD_DATA_BYTES); + } + + delete [] retval; + delete [] origVal; + delete [] raw; + + return ret; +} + + + + + + + +int slsReceiverTCPIPInterface::propix_read_frame(){ + ret=OK; + char fName[MAX_STR_LENGTH]=""; + int acquisitionIndex = -1; + int frameIndex= -1; + int i; + + + //retval is a full frame + int bufferSize = PROPIX_BUFFER_SIZE; + int onebuffersize = bufferSize/PROPIX_PACKETS_PER_FRAME; + int onedatasize = PROPIX_DATA_BYTES; + + char* raw; + int rnel = bufferSize/(sizeof(int)); + int* retval = new int[rnel]; + int* origVal = new int[rnel]; + //all initialized to 0 + for(i=0;igetFramesCaught()){ + startAcquisitionIndex=-1; + cout<<"haven't caught any frame yet"<readFrame(0,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"<> PROPIX_FRAME_INDEX_OFFSET); + bindex2 = ((uint32_t)(*((uint32_t*)((char*)(raw+onebuffersize)))))+1; + pindex2 =(bindex2 & PROPIX_PACKET_INDEX_MASK); + index2 =((bindex2 & PROPIX_FRAME_INDEX_MASK) >> PROPIX_FRAME_INDEX_OFFSET); +#ifdef VERYVERBOSE + cout << "index1:" << hex << index << endl; + cout << "index2:" << hex << index << endl; +#endif + + memcpy(origVal,raw + HEADER_SIZE_NUM_TOT_PACKETS,bufferSize); + raw=NULL; + + /*//ignore if half frame is missing + if ((bindex != 0xFFFFFFFF) && (bindex2 != 0xFFFFFFFF)){*/ + + //should be same frame + if (index == index2){ + //ideal situation (should be odd, even(index+1)) + if(!pindex){ + memcpy(retval,((char*) origVal)+4, onedatasize); + memcpy((((char*)retval)+onedatasize), ((char*) origVal)+10+onedatasize, onedatasize); + } + //swap to even,odd + else{ + memcpy((((char*)retval)+onedatasize),((char*) origVal)+4, onedatasize); + memcpy(retval, ((char*) origVal)+10+onedatasize, onedatasize); + index=index2; + } + }else + cout << "different frames caught. frame1:"<< hex << index << ":"<differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + else{ + mySock->SendDataOnly(fName,MAX_STR_LENGTH); + mySock->SendDataOnly(&acquisitionIndex,sizeof(acquisitionIndex)); + mySock->SendDataOnly(&frameIndex,sizeof(frameIndex)); + mySock->SendDataOnly(retval,PROPIX_DATA_BYTES); + } + + delete [] retval; + delete [] origVal; + delete [] raw; + + return ret; +} + + + + + + + + + + +int slsReceiverTCPIPInterface::eiger_read_frame(){ + ret=OK; +/* + char fName[MAX_STR_LENGTH]=""; + int acquisitionIndex = -1; + int frameIndex= -1; + int index=0; + int subframenumber=-1; + + 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 * 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](); + memset(origVal,0xFF,frameSize); + memset(retval,0xFF,dataSize); + + int64_t startAcquisitionIndex=0; + int64_t startFrameIndex=0; + strcpy(mess,"Could not read frame\n"); + + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + + if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + + //send garbage with -1 index to try again + else if(!receiverBase->getFramesCaught()){ + startAcquisitionIndex=-1; +#ifdef VERYVERBOSE + cout<<"haven't caught any frame yet"<readFrame(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 for "<< i << endl; +#endif + raw=NULL; + break; + } + else{ + eiger_packet_footer_t* wbuf_footer; + wbuf_footer = (eiger_packet_footer_t*)(raw + HEADER_SIZE_NUM_TOT_PACKETS + oneDataSize + sizeof(eiger_packet_header_t)); + index =(uint32_t)(*( (uint64_t*) wbuf_footer)); + index += (startFrameIndex-1); + fnum[i] = index; + if(dynamicrange == 32){ + eiger_packet_header_t* wbuf_header; + wbuf_header = (eiger_packet_header_t*) (raw + HEADER_SIZE_NUM_TOT_PACKETS); + subframenumber = *( (uint32_t*) wbuf_header->subFrameNumber); + } +#ifdef VERYVERBOSE + cout << "index:" << dec << index << endl; + cout << "subframenumber:" << dec << subframenumber << endl; +#endif + int numpackets = (uint32_t)(*( (uint32_t*) raw)); + memcpy(((char*)origVal)+(i*onePacketSize*packetsPerFrame),raw + HEADER_SIZE_NUM_TOT_PACKETS,numpackets*onePacketSize); + raw=NULL; + } + } + //proper 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 + int retindex=0; + int irow,ibytesperpacket; + int linesperpacket = (16*1/dynamicrange);// 16:1 line, 8:2 lines, 4:4 lines, 32: 0.5 + int numbytesperlineperport=(EIGER_PIXELS_IN_ONE_ROW/EIGER_MAX_PORTS)*dynamicrange/8;//16:1024,8:512,4:256,32:2048 + int datapacketlength = EIGER_ONE_GIGA_ONE_DATA_SIZE; + int total_num_bytes = EIGER_ONE_GIGA_ONE_PACKET_SIZE *(EIGER_ONE_GIGA_CONSTANT *dynamicrange)*2; + + if(tenGigaEnable){ + linesperpacket = (16*4/dynamicrange);// 16:4 line, 8:8 lines, 4:16 lines, 32: 2 + datapacketlength = EIGER_TEN_GIGA_ONE_DATA_SIZE; + total_num_bytes = EIGER_TEN_GIGA_ONE_PACKET_SIZE*(EIGER_TEN_GIGA_CONSTANT*dynamicrange)*2; + } + //if 1GbE, one line is split into two packets for 32 bit mode, so its special + else if(dynamicrange == 32){ + numbytesperlineperport = 1024; + linesperpacket = 1; //we repeat this twice anyway for 32 bit + } + + if(!bottom){ + + for(irow=0;irowdifferentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + else{ + mySock->SendDataOnly(fName,MAX_STR_LENGTH); + mySock->SendDataOnly(&acquisitionIndex,sizeof(acquisitionIndex)); + mySock->SendDataOnly(&frameIndex,sizeof(frameIndex)); + mySock->SendDataOnly(&subframenumber,sizeof(subframenumber)); + mySock->SendDataOnly(retval,dataSize); + } + + delete [] retval; + delete [] origVal; + delete [] raw; +*/ + return ret; +} + + + + + + + +int slsReceiverTCPIPInterface::jungfrau_read_frame(){ + ret=OK; + + char fName[MAX_STR_LENGTH]=""; + int acquisitionIndex = -1; + int frameIndex= -1; + int64_t currentIndex=0; + int64_t startAcquisitionIndex=0; + int64_t startFrameIndex=0; + strcpy(mess,"Could not read frame\n"); + + + int frameSize = JFRAU_ONE_PACKET_SIZE * packetsPerFrame; + int dataSize = JFRAU_ONE_DATA_SIZE * packetsPerFrame; + int oneDataSize = JFRAU_ONE_DATA_SIZE; + + char* raw; + char* origVal = new char[frameSize](); + char* retval = new char[dataSize](); + char* blackpacket = new char[oneDataSize](); + + for(int i=0;igetFramesCaught()){ + startAcquisitionIndex=-1; +#ifdef VERYVERBOSE + cout<<"haven't caught any frame yet"<readFrame(0,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"<frameNumber))&0xffffff; +#ifdef VERYVERBOSE + cout << "currentIndex:" << dec << currentIndex << endl; +#endif + + int64_t currentPacket = packetsPerFrame-1; + int offsetsrc = 0; + int offsetdest = 0; + int64_t ifnum=-1; + int64_t ipnum=-1; + + while(currentPacket >= 0){ + header = (jfrau_packet_header_t*) (origVal + offsetsrc); + ifnum = (*( (uint32_t*) header->frameNumber))&0xffffff; + ipnum = (*( (uint8_t*) header->packetNumber)); + if(ifnum != currentIndex) { + cout << "current packet " << currentPacket << " Wrong Frame number " << ifnum << ", copying blank packet" << endl; + memcpy(retval+offsetdest,blackpacket,oneDataSize); + offsetdest += oneDataSize; + //no need to increase offsetsrc as all packets will be wrong + currentPacket--; + continue; + } + if(ipnum!= currentPacket){ + cout << "current packet " << currentPacket << " Wrong packet number " << ipnum << ", copying blank packet" << endl; + memcpy(retval+offsetdest,blackpacket,oneDataSize); + offsetdest += oneDataSize; + //no need to increase offsetsrc until we get the right packet + currentPacket--; + continue; + } + offsetsrc+=JFRAU_HEADER_LENGTH; + memcpy(retval+offsetdest,origVal+offsetsrc,oneDataSize); + offsetdest += oneDataSize; + offsetsrc += oneDataSize; + currentPacket--; + } + + + acquisitionIndex = (int)(currentIndex-startAcquisitionIndex); + if(acquisitionIndex == -1) + startFrameIndex = -1; + else + frameIndex = (int)(currentIndex-startFrameIndex); +#ifdef VERY_VERY_DEBUG + cout << "acquisitionIndex calculated is:" << acquisitionIndex << endl; + cout << "frameIndex calculated is:" << frameIndex << endl; + cout << "currentIndex:" << currentIndex << endl; + cout << "startAcquisitionIndex:" << startAcquisitionIndex << endl; + cout << "startFrameIndex:" << startFrameIndex << endl; +#endif + } + } + +#ifdef VERYVERBOSE + if(frameIndex!=-1){ + cout << "fName:" << fName << endl; + cout << "acquisitionIndex:" << acquisitionIndex << endl; + cout << "frameIndex:" << frameIndex << endl; + cout << "startAcquisitionIndex:" << startAcquisitionIndex << endl; + cout << "startFrameIndex:" << startFrameIndex << endl; + } +#endif + + + +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + else{ + mySock->SendDataOnly(fName,MAX_STR_LENGTH); + mySock->SendDataOnly(&acquisitionIndex,sizeof(acquisitionIndex)); + mySock->SendDataOnly(&frameIndex,sizeof(frameIndex)); + mySock->SendDataOnly(retval,dataSize); + } + + delete [] retval; + delete [] origVal; + delete [] raw; + + return ret; +} + + + + + + +int slsReceiverTCPIPInterface::set_read_frequency(){ + ret=OK; + int retval=-1; + int index; + strcpy(mess,"Could not set receiver read frequency\n"); + + + // receive arguments + if(mySock->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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set receiver frequency mode while receiver not idle\n"); + cprintf(RED,"%s\n",mess); + ret = FAIL; + } + else{ + if(index >= 0 ){ + ret = receiverBase->setFrameToGuiFrequency(index); + 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){ + strcpy(mess,"Could not set frame to gui frequency"); + cprintf(RED,"%s\n",mess); + ret = FAIL; + } + } + } + +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + + +int slsReceiverTCPIPInterface::set_read_receiver_timer(){ + ret=OK; + int retval=-1; + int index; + strcpy(mess,"Could not set receiver read timer\n"); + + + // receive arguments + if(mySock->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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set receiver frequency mode while receiver not idle\n"); + cprintf(RED,"%s\n",mess); + ret = FAIL; + } + else{ + if(index >= 0 ){ + receiverBase->setFrameToGuiTimer(index); + } + retval=receiverBase->getFrameToGuiTimer(); + if(index>=0 && retval!=index){ + strcpy(mess,"Could not set datastream timer"); + cprintf(RED,"%s\n",mess); + ret = FAIL; + } + } + } + +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + +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(mySock->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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if((index >= 0) && (receiverBase->getStatus()!= IDLE)){ + 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 && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + +int slsReceiverTCPIPInterface::enable_file_write(){ + ret=OK; + int retval=-1; + int enable; + strcpy(mess,"Could not set/get enable file write\n"); + + + // receive arguments + if(mySock->ReceiveDataOnly(&enable,sizeof(enable)) < 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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set file write mode while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + if(enable >= 0) + receiverBase->setFileWriteEnable(enable); + retval=receiverBase->getFileWriteEnable(); + if(enable>=0 && enable!=retval) + ret=FAIL; + } + } +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + +int slsReceiverTCPIPInterface::get_id(){ + ret=OK; + int64_t retval=-1; + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + retval = getReceiverVersion(); +#endif + + if(mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + +int64_t slsReceiverTCPIPInterface::getReceiverVersion(){ + int64_t retval = SVNREV; + retval= (retval <<32) | SVNDATE; + return retval; +} + + + + +int slsReceiverTCPIPInterface::start_readout(){cprintf(BLUE,"In start readout!\n"); + ret=OK; + enum runStatus retval; + + // execute action if the arguments correctly arrived +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + /*else if(receiverBase->getStatus()!= IDLE){ + 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)) + ret = OK; + else + ret = FAIL; + } +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + //return ok/fail + return ret; + + +} + + + + +int slsReceiverTCPIPInterface::set_timer() { + ret=OK; + int64_t retval = -1; + int64_t index[2]; + index[1] = -1; + strcpy(mess,"Could not set acquisition period or frame number in receiver\n"); + + + // receive arguments + if(mySock->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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set timer while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + if(index[0] == ACQUISITION_TIME){ + if(index[1]>=0){ + ret = receiverBase->setAcquisitionTime(index[1]); + if(ret == FAIL) + strcpy(mess,"Could not allocate memory for listening fifo\n"); + } + retval=receiverBase->getAcquisitionTime(); + }else if(index[0] == FRAME_PERIOD){ + if(index[1]>=0){ + ret = receiverBase->setAcquisitionPeriod(index[1]); + if(ret == FAIL) + strcpy(mess,"Could not allocate memory for listening fifo\n"); + } + retval=receiverBase->getAcquisitionPeriod(); + }else{ + if(index[1]>=0) + receiverBase->setNumberOfFrames(index[1]); + retval=receiverBase->getNumberOfFrames(); + } + if(index[1]>=0 && retval!=index[1]) + ret = FAIL; + } + } +#ifdef VERYVERBOSE + if(ret!=FAIL){ + if(index[0] == ACQUISITION_TIME) + cout << "acquisition time:" << retval << endl; + else if(index[0] == FRAME_PERIOD) + cout << "acquisition period:" << retval << endl; + else + cout << "frame number:" << retval << endl; + }else + cout << mess << endl; +#endif +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + + +int slsReceiverTCPIPInterface::enable_compression() { + ret=OK; + int enable=-1; + int retval=-100; + strcpy(mess,"Could not enable/disable compression for receiver\n"); + + // receive arguments + if(mySock->ReceiveDataOnly(&enable,sizeof(enable)) < 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(enable >= 0){ + if (lockStatus==1 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Cannot enable/disable compression while status is running\n"); + cprintf(RED,"%s",mess); + ret=FAIL; + } + else{ + if(enable >= 0) + ret = receiverBase->setDataCompressionEnable(enable); + } + } + + if(ret != FAIL){ + if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + }else{ + retval = receiverBase->getDataCompressionEnable(); + if(enable >= 0 && retval != enable) + ret = FAIL; + } + } + + } +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + +int slsReceiverTCPIPInterface::set_detector_hostname() { + ret=OK; + char* retval = NULL; + char defaultVal[MAX_STR_LENGTH] = ""; + char hostname[MAX_STR_LENGTH]=""; + strcpy(mess,"Could not set detector hostname"); + + // receive arguments + if(mySock->ReceiveDataOnly(hostname,MAX_STR_LENGTH) < 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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set detector hostname while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + receiverBase->initialize(hostname); + retval = receiverBase->getDetectorHostname(); + if(retval == NULL){ + ret = FAIL; + } + } + } +#ifdef VERYVERBOSE + if(ret!=FAIL) + cout << "hostname:" << retval << endl; + else + cout << mess << endl; +#endif +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED, "%s\n", mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + if(retval == NULL) + mySock->SendDataOnly(defaultVal,MAX_STR_LENGTH); + else{ + mySock->SendDataOnly(retval,MAX_STR_LENGTH); + delete[] retval; + } + + //return ok/fail + return ret; +} + + + + + + +//LEO: why the receiver should set the dynamic range? +int slsReceiverTCPIPInterface::set_dynamic_range() { + ret=OK; + int retval=-1; + int dr; + strcpy(mess,"Could not set dynamic range\n"); + + + // receive arguments + if(mySock->ReceiveDataOnly(&dr,sizeof(dr)) < 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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (dr>0) { + switch(dr){ + case 4: + case 8: + case 16: + case 32:break; + default: + sprintf(mess,"This dynamic range does not exist: %d\n",dr); + cprintf(RED,"%s", mess); + ret=FAIL; + break; + } + } + if(ret!=FAIL){ + if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set dynamic range while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + if(dr > 0){ + ret = receiverBase->setDynamicRange(dr); + if(ret == FAIL) + strcpy(mess, "Could not allocate memory for fifo or could not start listening/writing threads\n"); + } + retval = receiverBase->getDynamicRange(); + if(dr > 0 && retval != dr) + ret = FAIL; + else{ + dynamicrange = retval; + if(myDetectorType == EIGER){ + if(!tenGigaEnable) + packetsPerFrame = EIGER_ONE_GIGA_CONSTANT * dynamicrange; + else + packetsPerFrame = EIGER_TEN_GIGA_CONSTANT * dynamicrange; + }else if (myDetectorType == JUNGFRAU) + packetsPerFrame = JFRAU_PACKETS_PER_FRAME; + } + } + } + } +#ifdef VERYVERBOSE + if(ret!=FAIL) + cout << "dynamic range" << dr << endl; + else + cout << mess << endl; +#endif +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + + + +int slsReceiverTCPIPInterface::enable_overwrite() { + ret=OK; + int retval=-1; + int index; + strcpy(mess,"Could not enable/disable overwrite\n"); + + + // receive arguments + if(mySock->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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set overwrite mode while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + if(index >= 0) + receiverBase->setOverwriteEnable(index); + retval=receiverBase->getOverwriteEnable(); + if(index >=0 && retval != index) + ret = FAIL; + } + } +#ifdef VERYVERBOSE + if(ret!=FAIL) + cout << "overwrite:" << retval << endl; + else + cout << mess << endl; +#endif +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + + + +int slsReceiverTCPIPInterface::enable_tengiga() { + ret=OK; + int retval=-1; + int val; + strcpy(mess,"Could not enable/disable 10Gbe\n"); + + + // receive arguments + if(mySock->ReceiveDataOnly(&val,sizeof(val)) < 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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set up 1Giga/10Giga mode while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + if(val >= 0) + ret = receiverBase->setTenGigaEnable(val); + retval=receiverBase->getTenGigaEnable(); + if((val >= 0) && (val != retval)) + ret = FAIL; + else + tenGigaEnable = retval; + } + } +#ifdef VERYVERBOSE + if(ret!=FAIL) + cout << "10Gbe:" << val << endl; + else + cout << mess << endl; +#endif +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + + +int slsReceiverTCPIPInterface::set_fifo_depth() { + ret=OK; + int value=-1; + int retval=-100; + strcpy(mess,"Could not set/get fifo depth for receiver\n"); + + // receive arguments + if(mySock->ReceiveDataOnly(&value,sizeof(value)) < 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(value >= 0){ + if (lockStatus==1 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Cannot set/get fifo depth while status is running\n"); + cprintf(RED,"%s",mess); + ret=FAIL; + } + else{ + if(value >= 0){ + ret = receiverBase->setFifoDepth(value); + } + } + } + + + if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + }else{ + retval = receiverBase->getFifoDepth(); + if(value >= 0 && retval != value) + ret = FAIL; + } + + } +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + +int slsReceiverTCPIPInterface::set_activate() { + ret=OK; + int retval=-1; + int enable; + strcpy(mess,"Could not activate/deactivate\n"); + + + // receive arguments + if(mySock->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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->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 && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + +int slsReceiverTCPIPInterface::set_flipped_data(){ + ret=OK; + int retval = -1; + int args[2]={0,-1}; + strcpy(mess,"Could not set flipped data in receiver\n"); + + + // receive arguments + if(mySock->ReceiveDataOnly(args,sizeof(args)) < 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 && mySock->differentClients==1){ + sprintf(mess,"Receiver locked by %s\n", mySock->lastClientIP); + ret=FAIL; + } + else if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + else if(receiverBase->getStatus()!= IDLE){ + strcpy(mess,"Can not set flipped data while receiver not idle\n"); + cprintf(RED,"%s",mess); + ret = FAIL; + } + else{ + if(args[1] > -1) + receiverBase->setFlippedData(args[0],args[1]); + retval=receiverBase->getFlippedData(args[0]); + } + } +#ifdef VERYVERBOSE + if(ret!=FAIL){ + cout << "Flipped Data:" << retval << endl; + }else + cout << mess << endl; +#endif +#endif + + if(ret==OK && mySock->differentClients){ + FILE_LOG(logDEBUG) << "Force update"; + ret=FORCE_UPDATE; + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + mySock->SendDataOnly(&retval,sizeof(retval)); + + //return ok/fail + return ret; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +int slsReceiverTCPIPInterface::lock_receiver() { + ret=OK; + int lock; + + // receive arguments + if(mySock->ReceiveDataOnly(&lock,sizeof(lock)) < 0 ){ + sprintf(mess,"Error reading from socket\n"); + cout << "Error reading from socket (lock)" << endl; + ret=FAIL; + } + // execute action if the arguments correctly arrived + if(ret==OK){ + if (lock>=0) { + if (lockStatus==0 || strcmp(mySock->lastClientIP,mySock->thisClientIP)==0 || strcmp(mySock->lastClientIP,"none")==0) { + lockStatus=lock; + strcpy(mySock->lastClientIP,mySock->thisClientIP); + } else { + ret=FAIL; + sprintf(mess,"Receiver already locked by %s\n", mySock->lastClientIP); + } + } + } + + if (mySock->differentClients && ret==OK) + ret=FORCE_UPDATE; + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + } + else + mySock->SendDataOnly(&lockStatus,sizeof(lockStatus)); + + //return ok/fail + return ret; +} + + + + + + + +int slsReceiverTCPIPInterface::set_port() { + ret=OK; + MySocketTCP* mySocket=NULL; + char oldLastClientIP[INET_ADDRSTRLEN]; + int sd=-1; + enum runStatus p_type; /* just to get the input */ + int p_number; + + // receive arguments + if(mySock->ReceiveDataOnly(&p_type,sizeof(p_type)) < 0 ){ + strcpy(mess,"Error reading from socket\n"); + cout << mess << endl; + ret=FAIL; + } + + if(mySock->ReceiveDataOnly(&p_number,sizeof(p_number)) < 0 ){ + strcpy(mess,"Error reading from socket\n"); + cout << mess << endl; + ret=FAIL; + } + + // execute action if the arguments correctly arrived + if (ret==OK) { + if (mySock->differentClients==1 && lockStatus==1 ) { + ret=FAIL; + sprintf(mess,"Detector locked by %s\n",mySock->lastClientIP); + } + else { + if (p_number<1024) { + sprintf(mess,"Too low port number %d\n", p_number); + cout << mess << endl; + ret=FAIL; + } + cout << "set port " << p_type << " to " << p_number <lastClientIP); + mySocket = new MySocketTCP(p_number); + } + if(mySocket){ + sd = mySocket->getErrorStatus(); + if (!sd){ + ret=OK; + strcpy(mySock->lastClientIP,oldLastClientIP); + if (mySocket->differentClients) + ret=FORCE_UPDATE; + } else { + ret=FAIL; + sprintf(mess,"Could not bind port %d\n", p_number); + cout << mess << endl; + if (sd==-10) { + sprintf(mess,"Port %d already set\n", p_number); + cout << mess << endl; + } + } + } + } + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + }else { + mySock->SendDataOnly(&p_number,sizeof(p_number)); + if(sd>=0){ + mySock->Disconnect(); + delete mySock; + mySock = mySocket; + } + } + + //return ok/fail + return ret; +} + + + + + + +int slsReceiverTCPIPInterface::get_last_client_ip() { + ret=OK; + + if (mySock->differentClients ) + ret=FORCE_UPDATE; + + mySock->SendDataOnly(&ret,sizeof(ret)); + mySock->SendDataOnly(mySock->lastClientIP,sizeof(mySock->lastClientIP)); + + return ret; +} + + + + + + + +int slsReceiverTCPIPInterface::send_update() { + ret=OK; + int ind; + char defaultVal[MAX_STR_LENGTH]=""; + char* path = NULL; + + mySock->SendDataOnly(mySock->lastClientIP,sizeof(mySock->lastClientIP)); + + //index +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + + ind=receiverBase->getFileIndex(); + + mySock->SendDataOnly(&ind,sizeof(ind)); +#endif + + //filepath +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + path = receiverBase->getFilePath(); +#endif + if(path == NULL) + mySock->SendDataOnly(defaultVal,MAX_STR_LENGTH); + else{ + mySock->SendDataOnly(path,MAX_STR_LENGTH); + delete[] path; + } + + + //filename +#ifdef SLS_RECEIVER_UDP_FUNCTIONS + path = receiverBase->getFileName(); +#endif + if(path == NULL) + mySock->SendDataOnly(defaultVal,MAX_STR_LENGTH); + else{ + mySock->SendDataOnly(path,MAX_STR_LENGTH); + delete[] path; + } + + if (lockStatus==0) { + strcpy(mySock->lastClientIP,mySock->thisClientIP); + } + + return ret; + + +} + + + + + + +int slsReceiverTCPIPInterface::update_client() { + ret=OK; + if (receiverBase == NULL){ + strcpy(mess,SET_RECEIVER_ERR_MESSAGE); + ret=FAIL; + } + mySock->SendDataOnly(&ret,sizeof(ret)); + if(ret==FAIL){ + cprintf(RED,"%s\n",mess); + mySock->SendDataOnly(mess,sizeof(mess)); + return ret; + } + + return send_update(); +} + + + + + + + +int slsReceiverTCPIPInterface::exit_server() { + ret=GOODBYE; + mySock->SendDataOnly(&ret,sizeof(ret)); + strcpy(mess,"closing server"); + mySock->SendDataOnly(mess,sizeof(mess)); + cprintf(RED,"%s\n",mess); + return ret; +} + + + + + +int slsReceiverTCPIPInterface::exec_command() { + ret = OK; + char cmd[MAX_STR_LENGTH]; + char answer[MAX_STR_LENGTH]; + int sysret=0; + + // receive arguments + if(mySock->ReceiveDataOnly(cmd,MAX_STR_LENGTH) < 0 ){ + sprintf(mess,"Error reading from socket\n"); + ret=FAIL; + } + + // execute action if the arguments correctly arrived + if (ret==OK) { +#ifdef VERYVERBOSE + cout << "executing command " << cmd << endl; +#endif + if (lockStatus==0 || mySock->differentClients==0) + sysret=system(cmd); + + //should be replaced by popen + if (sysret==0) { + strcpy(answer,"Succeeded\n"); + if (lockStatus==1 && mySock->differentClients==1) + sprintf(answer,"Detector locked by %s\n", mySock->lastClientIP); + } else { + strcpy(answer,"Failed\n"); + ret=FAIL; + } + } else + strcpy(answer,"Could not receive the command\n"); + + + // send answer + mySock->SendDataOnly(&ret,sizeof(ret)); + if(mySock->SendDataOnly(answer,MAX_STR_LENGTH) < 0){ + strcpy(mess,"Error writing to socket"); + ret=FAIL; + } + + //return ok/fail + return ret; +} + + + +/***callback functions***/ +void slsReceiverTCPIPInterface::registerCallBackStartAcquisition(int (*func)(char*, char*,int, int, void*),void *arg){ + startAcquisitionCallBack=func; + pStartAcquisition=arg; +} + +void slsReceiverTCPIPInterface::registerCallBackAcquisitionFinished(void (*func)(int, void*),void *arg){ + acquisitionFinishedCallBack=func; + pAcquisitionFinished=arg; +} + +void slsReceiverTCPIPInterface::registerCallBackRawDataReady(void (*func)(int, char*, int, FILE*, char*, void*),void *arg){ + rawDataReadyCallBack=func; + pRawDataReady=arg; +} + + + + + diff --git a/slsReceiverSoftware/src/slsReceiverUsers.cpp b/slsReceiverSoftware/src/slsReceiverUsers.cpp new file mode 100644 index 000000000..8a1d17f43 --- /dev/null +++ b/slsReceiverSoftware/src/slsReceiverUsers.cpp @@ -0,0 +1,45 @@ +#include "slsReceiverUsers.h" +#include "slsReceiver.h" + +slsReceiverUsers::slsReceiverUsers(int argc, char *argv[], int &success) { + receiver=new slsReceiver(argc, argv, success); +} + +slsReceiverUsers::~slsReceiverUsers() { + delete receiver; +} + +int slsReceiverUsers::start() { + return receiver->start(); +} + +void slsReceiverUsers::stop() { + receiver->stop(); +} + + +void slsReceiverUsers::closeFile(int p) { + receiver->closeFile(p); +} + +int64_t slsReceiverUsers::getReceiverVersion(){ + return receiver->getReceiverVersion(); +} + + +void slsReceiverUsers::registerCallBackStartAcquisition(int (*func)(char*, char*,int, int, void*),void *arg){ + receiver->registerCallBackStartAcquisition(func,arg); +} + + + +void slsReceiverUsers::registerCallBackAcquisitionFinished(void (*func)(int, void*),void *arg){ + receiver->registerCallBackAcquisitionFinished(func,arg); +} + + +void slsReceiverUsers::registerCallBackRawDataReady(void (*func)(int, char*, int, FILE*, char*, void*),void *arg){ + receiver->registerCallBackRawDataReady(func,arg); +} + + diff --git a/slsReceiverSoftware/src/utilities.cpp b/slsReceiverSoftware/src/utilities.cpp new file mode 100644 index 000000000..afb5a6c65 --- /dev/null +++ b/slsReceiverSoftware/src/utilities.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "utilities.h" + + +using namespace std; + + +int read_config_file(string fname, int *tcpip_port_no, map * configuration_map ){ + + ifstream infile; + string sLine,sargname, sargvalue; + int iline = 0; + int success = slsReceiverDefs::OK; + + + + infile.open(fname.c_str(), ios_base::in); + if (infile.is_open()) { + while(infile.good()){ + getline(infile,sLine); + iline++; + + //VERBOSE_PRINT(sLine); + + if(sLine.find('#') != string::npos) + continue; + + else if(sLine.length()<2) + continue; + + else{ + istringstream sstr(sLine); + + //parameter name + if(sstr.good()){ + sstr >> sargname; + + if (! sstr.good()) + continue; + + sstr >> sargvalue; + (*configuration_map)[sargname] = sargvalue; + } + //tcp port + if(sargname=="rx_tcpport"){ + if(sstr.good()) { + sstr >> sargname; + if(sscanf(sargname.c_str(),"%d",tcpip_port_no)) + cout<<"dataport:"< +#include "MySocketTCP.h" + +using namespace std; + +int main(){ + + char data[50000]; + int length=50000; + + unsigned short int portnum = 1952; + MySocketTCP* sock = new MySocketTCP(portnum); + + cout<<"\tReceived :"<ReceiveDataAndKeepConnection(data,23000)<ReceiveData(data,32200)<ReceiveData(data,33300)<ReceiveData(data,30000)<ReceiveData(data,3222)< +#include "MySocketTCP.h" + +using namespace std; + +int main(int argc, char *argv[]){ + + if(argc!=2){ + cout<<"Usage: send ip_addess/hostName"<SendDataAndKeepConnection(data,2000)<SendData(data,2200)<SendData(data,1200)<SendData(data,25000)<SendData(data,222)<