From 7d7ab1782e54449a7b8fe0700c569f71e57d0221 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Tue, 7 Jun 2016 21:14:15 +0200 Subject: [PATCH 01/24] Update README.md --- README.md | 91 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 0123945..a656940 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,31 @@ Single Channel LoRaWAN Gateway ============================== -This repository contains a proof-of-concept implementation of a single -channel LoRaWAN gateway. +This repository contains a proof-of-concept implementation of a dual +channel LoRaWAN gateway with the Raspberry Pi+ LoRa(TM) Expansion Board of +Uputronics. It is based on the Single Channel LoRaWan Gateway +by Thomas Telkamp. -It has been tested on the Raspberry Pi platform, using a Semtech SX1272 -transceiver (HopeRF RFM92W), and SX1276 (HopeRF RFM95W). - -The code is for testing and development purposes only, and is not meant -for production usage. +It has been tested on the Raspberry Pi platform, using a Raspberry Pi+ +LoRa(TM) Expansion Board with 2 SX1276 (HopeRF RFM95W) on board. Part of the source has been copied from the Semtech Packet Forwarder (with permission). Maintainer: Thomas Telkamp +The code is for testing and development purposes only, and is not meant +for production usage. + +I will continue update this code for our own purposes; Hans Boksem (hans.boksem@gmail.com) + Features -------- - listen on configurable frequency and spreading factor - SF7 to SF12 - status updates - can forward to two servers +- Uses 2 channels (as the board has 2 RFM95's), default set to 868.1 and 868.3 frequencies +- Autostart features Not (yet) supported: - PACKET_PUSH_ACK processing @@ -27,35 +33,66 @@ Not (yet) supported: - FSK modulation - downstream messages (tx) -Dependencies ------------- -- SPI needs to be enabled on the Raspberry Pi (use raspi-config) -- WiringPi: a GPIO access library written in C for the BCM2835 - used in the Raspberry Pi. - sudo apt-get install wiringpi - see http://wiringpi.com -- Run packet forwarder as root +Preparation, setup and running +------------------------------ + +Prepare SD Card +- download raspbian jessie lite +- unpack zip +- write img file with win32DiskImager or alike + +Prepare Pi +- Insert SD card +- Start raspberry pi +- figure out the IP address (look into the routers DHCP config or attach monitor and find out there) +- login to the rasp (un/pw pi/raspberry) +- change the password (with passwd) +- run raspi-config (sudo raspi-config) +- Expand the filesystem +- Goto advanced config and select SPI +- Enable SPI interface +- Enable loading of SPI kernel module by default +- exit the raspi-config and reboot + +Install dependencies +- update the pkg database (twice command: sudo apt-get update) +- install wiringpi (sudo apt-get wiringpi) + +Install dual_chan_pkt_fw +- Put the zip file on the pi (using winscp or alike) +- unzip the file +- cd into the dual_chan_pkt_fw folder +- change the main.cpp with your gateway parameters (Informal status fields; platform, email, description) and location (lat,lon,alt)) +- compile the dual_chan_pkt_fwd (make) + +Install autostart dual_chan_pkt_fw +- copy the lora_gateway.sh script to /etc/init.d (sudo cp lora_gateway.sh /etc/init.d) +- set execute permission on lora_gateway.sh (sudo chmod a+x /etc/init.d/lora_gateway.sh) +- set execute permission on lora_gw_startup.sh (sudo chmod a+x lora_gw_startup.sh) +- configure the autostart (sudo update-rc.d lora_gateway.sh defaults) + +Starting the dual_chan_pkt_fwd +- to start the dual_chan_pkt_fwd reboot or start via the commandline (/etc/init.d/lora_gateway.sh restart) +- Remember; it takes about 10 seconds to start the gateway... + +Check the log +- Logging is done within the file /var/log/lora_gateway +- Remember; it takes about 10 seconds to start the gateway... + +Troubleshooting +- When after rebooting one of the LEDs on the Hat (INTERNET) is on continuosly this means there is something wrong with the network. Fix the network, login and restart the lora_gateway Connections ----------- -SX1272 - Raspberry - -3.3V - 3.3V (header pin #1) -GND - GND (pin #6) -MISO - MISO (pin #21) -MOSI - MOSI (pin #19) -SCK - CLK (pin #23) -NSS - GPIO6 (pin #22) -DIO0 - GPIO7 (pin #7) -RST - GPIO0 (pin #11) +It is a raspberry Pi hat so just pop it on an rasp 2 or 3 Configuration ------------- Defaults: -- LoRa: SF7 at 868.1 Mhz -- Server: 54.229.214.112, port 1700 (The Things Network: croft.thethings.girovito.nl) +- LoRa: SF7 at 868.1 and 868.3 Mhz +- Server: 40.114.249.243 port 1700 (The Things Network: router.eu.thethings.network) Edit source node (main.cpp) to change configuration (look for: "Configure these values!"). From 7c2b19f45dabfec27bf6ab17879523aeab6befa7 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Tue, 7 Jun 2016 21:18:28 +0200 Subject: [PATCH 02/24] Update main.cpp --- main.cpp | 301 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 203 insertions(+), 98 deletions(-) diff --git a/main.cpp b/main.cpp index c669f3f..d390a63 100644 --- a/main.cpp +++ b/main.cpp @@ -1,12 +1,12 @@ /******************************************************************************* - * * Copyright (c) 2015 Thomas Telkamp * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html * + * Changes by Hans Boksem for creating a dual channel gateway with the Raspberry + * Pi+ LoRa(TM) * Expansion Board * of Uputronics. *******************************************************************************/ #include @@ -62,33 +62,49 @@ enum sf_t { SF7=7, SF8, SF9, SF10, SF11, SF12 }; * *******************************************************************************/ -// SX1272 - Raspberry connections -int ssPin = 6; -int dio0 = 7; +// SX1276 - Uputronics Raspberry Pi+ LoRa(TM) Expansion Board connections +// CE0 +int ssPin = 10; +int dio0 = 6; +// CE1 +int ssPin_2 = 11; +int dio0_2 = 27; + int RST = 0; +//LEDs +int NetworkLED = 22; +int InternetLED = 23; +int ActivityLED_0 = 21; +int ActivityLED_1 = 29; + // Set spreading factor (SF7 - SF12) sf_t sf = SF7; // Set center frequency uint32_t freq = 868100000; // in Mhz! (868.1) +uint32_t freq_2 = 868300000; // in Mhz! (868.3) // Set location -float lat=0.0; -float lon=0.0; -int alt=0; +float lat=; +float lon=; +int alt=; /* Informal status fields */ -static char platform[24] = "Single Channel Gateway"; /* platform definition */ -static char email[40] = ""; /* used for contact email */ -static char description[64] = ""; /* used for free form description */ +static char platform[24] = ""; /* platform definition */ +static char email[40] = ""; /* used for contact email */ +static char description[64] = ">16) ); - writeRegister(REG_FRF_MID, (uint8_t)(frf>> 8) ); - writeRegister(REG_FRF_LSB, (uint8_t)(frf>> 0) ); + uint64_t frf; + if (CE == 0) + { + frf = ((uint64_t)freq << 19) / 32000000; + } else { + frf = ((uint64_t)freq_2 << 19) / 32000000; + } + + writeRegister(REG_FRF_MSB, (uint8_t)(frf>>16) , CE); + writeRegister(REG_FRF_MID, (uint8_t)(frf>> 8) , CE); + writeRegister(REG_FRF_LSB, (uint8_t)(frf>> 0) , CE); - writeRegister(REG_SYNC_WORD, 0x34); // LoRaWAN public sync word + writeRegister(REG_SYNC_WORD, 0x34, CE); // LoRaWAN public sync word + //printf("written MSB: %f\n", (double)(frf>>16)); + //printf("written MID: %f\n", (double)(frf>>8)); + //printf("written LSB: %f\n", (double)(frf>>0)); if (sx1272) { if (sf == SF11 || sf == SF12) { - writeRegister(REG_MODEM_CONFIG,0x0B); + writeRegister(REG_MODEM_CONFIG,0x0B, CE); } else { - writeRegister(REG_MODEM_CONFIG,0x0A); + writeRegister(REG_MODEM_CONFIG,0x0A, CE); } - writeRegister(REG_MODEM_CONFIG2,(sf<<4) | 0x04); + writeRegister(REG_MODEM_CONFIG2,(sf<<4) | 0x04, CE); } else { if (sf == SF11 || sf == SF12) { - writeRegister(REG_MODEM_CONFIG3,0x0C); + writeRegister(REG_MODEM_CONFIG3,0x0C, CE); } else { - writeRegister(REG_MODEM_CONFIG3,0x04); + writeRegister(REG_MODEM_CONFIG3,0x04, CE); } - writeRegister(REG_MODEM_CONFIG,0x72); - writeRegister(REG_MODEM_CONFIG2,(sf<<4) | 0x04); + writeRegister(REG_MODEM_CONFIG,0x72, CE); + writeRegister(REG_MODEM_CONFIG2,(sf<<4) | 0x04, CE); } if (sf == SF10 || sf == SF11 || sf == SF12) { - writeRegister(REG_SYMB_TIMEOUT_LSB,0x05); + writeRegister(REG_SYMB_TIMEOUT_LSB,0x05, CE); } else { - writeRegister(REG_SYMB_TIMEOUT_LSB,0x08); + writeRegister(REG_SYMB_TIMEOUT_LSB,0x08, CE); } - writeRegister(REG_MAX_PAYLOAD_LENGTH,0x80); - writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH); - writeRegister(REG_HOP_PERIOD,0xFF); - writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_BASE_AD)); + writeRegister(REG_MAX_PAYLOAD_LENGTH,0x80, CE); + writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH, CE); + writeRegister(REG_HOP_PERIOD,0xFF, CE); + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_BASE_AD, CE), CE); // Set Continous Receive Mode - writeRegister(REG_LNA, LNA_MAX_GAIN); // max lna gain - writeRegister(REG_OPMODE, SX72_MODE_RX_CONTINUOS); - + writeRegister(REG_LNA, LNA_MAX_GAIN, CE); // max lna gain + writeRegister(REG_OPMODE, SX72_MODE_RX_CONTINUOS, CE); + + //byte test = readRegister(REG_FRF_MSB, CE); + //printf("Register read: %i\n", test); + //test = readRegister(REG_FRF_MID, CE); + //printf("Register read: %i\n", test); + //test = readRegister(REG_FRF_LSB, CE); + //printf("Register read: %i\n", test); } void sendudp(char *msg, int length) { @@ -318,8 +362,10 @@ void sendudp(char *msg, int length) { //send the update #ifdef SERVER1 inet_aton(SERVER1 , &si_other.sin_addr); + if (sendto(s, (char *)msg, length, 0 , (struct sockaddr *) &si_other, slen)==-1) { + writelog("DIE: Could not send update...\n"); die("sendto()"); } #endif @@ -331,6 +377,7 @@ void sendudp(char *msg, int length) { die("sendto()"); } #endif + } void sendstat() { @@ -338,9 +385,11 @@ void sendstat() { static char status_report[STATUS_SIZE]; /* status report as a JSON object */ char stat_timestamp[24]; time_t t; + char str[400]; int stat_index=0; + digitalWrite(InternetLED, HIGH); /* pre-fill the data buffer with fixed fields */ status_report[0] = PROTOCOL_VERSION; status_report[3] = PKT_PUSH_DATA; @@ -369,22 +418,39 @@ void sendstat() { stat_index += j; status_report[stat_index] = 0; /* add string terminator, for safety */ - printf("stat update: %s\n", (char *)(status_report+12)); /* DEBUG: display JSON stat */ //send the update sendudp(status_report, stat_index); + digitalWrite(InternetLED, LOW); + sprintf(str, "stat update: %s\n", (char *)(status_report+12)); /* DEBUG: display JSON stat */ + writelog(str); /* DEBUG: display JSON stat */ } -void receivepacket() { +void receivepacket(byte CE) { long int SNR; - int rssicorr; + int rssicorr, dio_port; + char str[200]; - if(digitalRead(dio0) == 1) + if (CE == 0) { - if(receivePkt(message)) { - byte value = readRegister(REG_PKT_SNR_VALUE); + dio_port = dio0; + } else { + dio_port = dio0_2; + } + + if(digitalRead(dio_port) == 1) + { + if(receivePkt(message, CE)) { + byte value = readRegister(REG_PKT_SNR_VALUE, CE); + if (CE == 0) + { + digitalWrite(ActivityLED_0, HIGH); + } else { + digitalWrite(ActivityLED_1, HIGH); + } + if( value & 0x80 ) // The SNR sign bit is 1 { // Invert and divide by 4 @@ -396,18 +462,23 @@ void receivepacket() { // Divide by 4 SNR = ( value & 0xFF ) >> 2; } - + if (sx1272) { rssicorr = 139; } else { rssicorr = 157; } - printf("Packet RSSI: %d, ",readRegister(0x1A)-rssicorr); - printf("RSSI: %d, ",readRegister(0x1B)-rssicorr); - printf("SNR: %li, ",SNR); - printf("Length: %i",(int)receivedbytes); - printf("\n"); + sprintf(str,"CE%i Packet RSSI: %d, ",CE,readRegister(0x1A, CE)-rssicorr); + writelog(str); + sprintf(str,"RSSI: %d, ",readRegister(0x1B, CE)-rssicorr); + writelog(str); + sprintf(str,"SNR: %li, ",SNR); + writelog(str); + sprintf(str,"Length: %i",(int)receivedbytes); + writelog(str); + sprintf(str,"\n"); + writelog(str); int j; j = bin_to_b64((uint8_t *)message, receivedbytes, (char *)(b64), 341); @@ -458,7 +529,12 @@ void receivepacket() { ++buff_index; j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, "\"tmst\":%u", tmst); buff_index += j; - j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%.6lf", 0, 0, (double)freq/1000000); + if (CE == 0) + { + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%.6lf", 0, 0, (double)freq/1000000); + } else { + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%.6lf", 1, 1, (double)freq_2/1000000); + } buff_index += j; memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":1", 9); buff_index += 9; @@ -500,7 +576,7 @@ void receivepacket() { buff_index += 13; j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"lsnr\":%li", SNR); buff_index += j; - j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"rssi\":%d,\"size\":%u", readRegister(0x1A)-rssicorr, receivedbytes); + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"rssi\":%d,\"size\":%u", readRegister(0x1A, CE)-rssicorr, receivedbytes); buff_index += j; memcpy((void *)(buff_up + buff_index), (void *)",\"data\":\"", 9); buff_index += 9; @@ -519,12 +595,19 @@ void receivepacket() { ++buff_index; buff_up[buff_index] = 0; /* add string terminator, for safety */ - printf("rxpk update: %s\n", (char *)(buff_up + 12)); /* DEBUG: display JSON payload */ - //send the messages sendudp(buff_up, buff_index); + sprintf(str,"rxpk update: %s\n", (char *)(buff_up + 12)); /* DEBUG: display JSON payload */ + writelog(str); fflush(stdout); + //printf("CE%i read packet\n",CE); + //byte test = readRegister(REG_FRF_MSB, CE); + //printf("Register read: %i\n", test); + //test = readRegister(REG_FRF_MID, CE); + //printf("Register read: %i\n", test); + //test = readRegister(REG_FRF_LSB, CE); + //printf("Register read: %i\n", test); } // received a message @@ -538,14 +621,29 @@ int main () { wiringPiSetup () ; pinMode(ssPin, OUTPUT); + pinMode(ssPin_2, OUTPUT); pinMode(dio0, INPUT); + pinMode(dio0_2, INPUT); pinMode(RST, OUTPUT); + pinMode(NetworkLED, OUTPUT); + pinMode(ActivityLED_0, OUTPUT); + pinMode(ActivityLED_1, OUTPUT); + pinMode(InternetLED, OUTPUT); + + char str[200]; + - //int fd = - wiringPiSPISetup(CHANNEL, 500000); + //int fd = + wiringPiSPISetup(0, 500000); + wiringPiSPISetup(1, 500000); //cout << "Init result: " << fd << endl; - SetupLoRa(); + digitalWrite(RST, HIGH); + delay(100); + digitalWrite(RST, LOW); + delay(100); + SetupLoRa(0); + SetupLoRa(1); if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { @@ -560,20 +658,28 @@ int main () { ioctl(s, SIOCGIFHWADDR, &ifr); /* display result */ - printf("Gateway ID: %.2x:%.2x:%.2x:ff:ff:%.2x:%.2x:%.2x\n", + sprintf(str, "Gateway ID: %.2x:%.2x:%.2x:ff:ff:%.2x:%.2x:%.2x\n", (unsigned char)ifr.ifr_hwaddr.sa_data[0], (unsigned char)ifr.ifr_hwaddr.sa_data[1], (unsigned char)ifr.ifr_hwaddr.sa_data[2], (unsigned char)ifr.ifr_hwaddr.sa_data[3], (unsigned char)ifr.ifr_hwaddr.sa_data[4], (unsigned char)ifr.ifr_hwaddr.sa_data[5]); + writelog(str); - printf("Listening at SF%i on %.6lf Mhz.\n", sf,(double)freq/1000000); - printf("------------------\n"); + sprintf(str,"Listening at SF%i on %.6lf Mhz on CE0.\n", sf,(double)freq/1000000); + writelog(str); + sprintf(str,"Listening at SF%i on %.6lf Mhz on CE1.\n", sf,(double)freq_2/1000000); + writelog(str); + sprintf(str,"------------------\n"); + writelog(str); while(1) { - receivepacket(); + digitalWrite(ActivityLED_0, LOW); + digitalWrite(ActivityLED_1, LOW); + receivepacket(0); + receivepacket(1); gettimeofday(&nowtime, NULL); uint32_t nowseconds = (uint32_t)(nowtime.tv_sec); @@ -586,7 +692,6 @@ int main () { } delay(1); } - return (0); } From 08fdcccd47b941eab7546863f97954367f6ca866 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Tue, 7 Jun 2016 21:19:48 +0200 Subject: [PATCH 03/24] Add files via upload --- lora_gateway.sh | 37 +++++++++++++++++++++++++++++++++++++ lora_gw_startup.sh | 7 +++++++ 2 files changed, 44 insertions(+) create mode 100644 lora_gateway.sh create mode 100644 lora_gw_startup.sh diff --git a/lora_gateway.sh b/lora_gateway.sh new file mode 100644 index 0000000..db8e930 --- /dev/null +++ b/lora_gateway.sh @@ -0,0 +1,37 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: Lora gateway +# Required-Start: $all +# Required-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Manage my cool stuff +### END INIT INFO + +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin + +. /lib/init/vars.sh +. /lib/lsb/init-functions +# If you need to source some other scripts, do it here + +case "$1" in + start) + log_begin_msg "Starting the lora gateway" + mv /var/log/lora_gateway /var/log/lora_gateway.bkp + /home/pi/dual_chan_pkt_fwd/lora_gw_startup.sh& + log_end_msg $? + exit 0 + ;; + stop) + log_begin_msg "Stopping the lora gateway" + + kill `ps -ef|grep dual_chan_pkt|grep -v grep| awk '{print $2}'` + + log_end_msg $? + exit 0 + ;; + *) + echo "Usage: /etc/init.d/ {start|stop}" + exit 1 + ;; +esac diff --git a/lora_gw_startup.sh b/lora_gw_startup.sh new file mode 100644 index 0000000..61cde54 --- /dev/null +++ b/lora_gw_startup.sh @@ -0,0 +1,7 @@ +!/bin/bash + +# lets wait a couple of seconds for the system to start and all is up +sleep 10 + +# Now start the lora gateway +/home/pi/dual_chan_pkt_fwd/dual_chan_pkt_fwd From 31b49fb77d283473e6203ef520eecb3154dc9461 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Tue, 7 Jun 2016 21:21:06 +0200 Subject: [PATCH 04/24] Update Makefile --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 228e00f..fd9b49f 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ -# single_chan_pkt_fwd -# Single Channel LoRaWAN Gateway +# dual_chan_pkt_fwd +# Dual Channel LoRaWAN Gateway CC=g++ CFLAGS=-c -Wall LIBS=-lwiringPi -all: single_chan_pkt_fwd +all: dual_chan_pkt_fwd -single_chan_pkt_fwd: base64.o main.o - $(CC) main.o base64.o $(LIBS) -o single_chan_pkt_fwd +dual_chan_pkt_fwd: base64.o main.o + $(CC) main.o base64.o $(LIBS) -o dual_chan_pkt_fwd main.o: main.cpp $(CC) $(CFLAGS) main.cpp @@ -17,4 +17,4 @@ base64.o: base64.c $(CC) $(CFLAGS) base64.c clean: - rm *.o single_chan_pkt_fwd \ No newline at end of file + rm *.o dual_chan_pkt_fwd From d7191d39ab87ac197011e9d83f0a374bf33d7458 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Tue, 7 Jun 2016 21:22:14 +0200 Subject: [PATCH 05/24] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a656940..6e09cec 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Single Channel LoRaWAN Gateway ============================== This repository contains a proof-of-concept implementation of a dual channel LoRaWAN gateway with the Raspberry Pi+ LoRa(TM) Expansion Board of -Uputronics. It is based on the Single Channel LoRaWan Gateway +Uputronics. It is based on (fork) the Single Channel LoRaWan Gateway by Thomas Telkamp. It has been tested on the Raspberry Pi platform, using a Raspberry Pi+ @@ -26,6 +26,7 @@ Features - can forward to two servers - Uses 2 channels (as the board has 2 RFM95's), default set to 868.1 and 868.3 frequencies - Autostart features +- logging to logfile (default /var.log/lora_gateway) Not (yet) supported: - PACKET_PUSH_ACK processing From 917f2296a55f0dc8e58c75186290e25aade0fe7f Mon Sep 17 00:00:00 2001 From: bokse001 Date: Tue, 7 Jun 2016 21:22:47 +0200 Subject: [PATCH 06/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e09cec..7591d76 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Features - can forward to two servers - Uses 2 channels (as the board has 2 RFM95's), default set to 868.1 and 868.3 frequencies - Autostart features -- logging to logfile (default /var.log/lora_gateway) +- logging to logfile (default /var/log/lora_gateway) Not (yet) supported: - PACKET_PUSH_ACK processing From a06f5a86c6f962ab1db5b810ca2a8dd3c6377d1d Mon Sep 17 00:00:00 2001 From: bokse001 Date: Tue, 7 Jun 2016 21:31:00 +0200 Subject: [PATCH 07/24] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7591d76..0872ed5 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Features - Uses 2 channels (as the board has 2 RFM95's), default set to 868.1 and 868.3 frequencies - Autostart features - logging to logfile (default /var/log/lora_gateway) +- Uses Hat LED's for indicating INTERNET, CEO and CE1 Data activity Not (yet) supported: - PACKET_PUSH_ACK processing From cef0c92d318f8107348698dfe3eeb358a5f7c9a9 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Tue, 14 Jun 2016 08:30:04 +0200 Subject: [PATCH 08/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0872ed5..3b2f322 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Install dependencies - install wiringpi (sudo apt-get wiringpi) Install dual_chan_pkt_fw -- Put the zip file on the pi (using winscp or alike) +- Put the zip file on the pi (using winscp or alike) or use git to clone the repository (git clone https://github.com/bokse001/dual_chan_pkt_fwd) - unzip the file - cd into the dual_chan_pkt_fw folder - change the main.cpp with your gateway parameters (Informal status fields; platform, email, description) and location (lat,lon,alt)) From 39c191516f8162e7e9794ae8a6f661c3eda8a855 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Tue, 14 Jun 2016 08:30:21 +0200 Subject: [PATCH 09/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b2f322..d5b5d34 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Single Channel LoRaWAN Gateway +Dual Channel LoRaWAN Gateway ============================== This repository contains a proof-of-concept implementation of a dual channel LoRaWAN gateway with the Raspberry Pi+ LoRa(TM) Expansion Board of From a09639e54afea50155eb89704fbb4332ee3633e8 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Thu, 30 Jun 2016 13:16:07 +0200 Subject: [PATCH 10/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5b5d34..933caa0 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Prepare Pi Install dependencies - update the pkg database (twice command: sudo apt-get update) -- install wiringpi (sudo apt-get wiringpi) +- install wiringpi (sudo apt-get install wiringpi) Install dual_chan_pkt_fw - Put the zip file on the pi (using winscp or alike) or use git to clone the repository (git clone https://github.com/bokse001/dual_chan_pkt_fwd) From 1bb25d63cc1102bb85cdc1b51db7dc85b82eb5af Mon Sep 17 00:00:00 2001 From: bokse001 Date: Fri, 23 Sep 2016 14:00:14 +0200 Subject: [PATCH 11/24] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 933caa0..8784d96 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ Defaults: - Server: 40.114.249.243 port 1700 (The Things Network: router.eu.thethings.network) Edit source node (main.cpp) to change configuration (look for: "Configure these values!"). +If you want to use wifi change the following line to represent you wifi device: +strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1); Please set location, email and description. From 452c44f90c640bef703a98190030cdde451a85df Mon Sep 17 00:00:00 2001 From: bokse001 Date: Thu, 27 Oct 2016 14:12:09 +0200 Subject: [PATCH 12/24] Forked of from hallard and performed changes for dual channel support Dual channel support with uputronics Raspberry Pi+ LoRa(TM) Expansion Board --- LICENSE | 52 +-- Makefile | 52 ++- README.md | 312 ++++++++----- base64.c | 616 ++++++++++++------------ base64.h | 124 ++--- dual_chan_pkt_fwd.cpp | 957 ++++++++++++++++++++++++++++++++++++++ dual_chan_pkt_fwd.service | 14 + global_conf.json | 44 ++ 8 files changed, 1646 insertions(+), 525 deletions(-) create mode 100644 dual_chan_pkt_fwd.cpp create mode 100644 dual_chan_pkt_fwd.service create mode 100644 global_conf.json diff --git a/LICENSE b/LICENSE index 6bc539e..eefd8a8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,26 +1,26 @@ -Applicable to base64.h and base64.c: - -Copyright (C) 2013, SEMTECH S.A. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Semtech corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. 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. +Applicable to base64.h and base64.c: + +Copyright (C) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Semtech corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. 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. diff --git a/Makefile b/Makefile index fd9b49f..f62f321 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,32 @@ -# dual_chan_pkt_fwd -# Dual Channel LoRaWAN Gateway - -CC=g++ -CFLAGS=-c -Wall -LIBS=-lwiringPi - -all: dual_chan_pkt_fwd - -dual_chan_pkt_fwd: base64.o main.o - $(CC) main.o base64.o $(LIBS) -o dual_chan_pkt_fwd - -main.o: main.cpp - $(CC) $(CFLAGS) main.cpp - -base64.o: base64.c - $(CC) $(CFLAGS) base64.c - -clean: - rm *.o dual_chan_pkt_fwd +# dual_chan_pkt_fwd +# Dual Channel LoRaWAN Gateway + +CC = g++ +CFLAGS = -std=c++11 -c -Wall -I include/ +LIBS = -lwiringPi + +all: dual_chan_pkt_fwd + +dual_chan_pkt_fwd: base64.o dual_chan_pkt_fwd.o + $(CC) dual_chan_pkt_fwd.o base64.o $(LIBS) -o dual_chan_pkt_fwd + +dual_chan_pkt_fwd.o: dual_chan_pkt_fwd.cpp + $(CC) $(CFLAGS) dual_chan_pkt_fwd.cpp + +base64.o: base64.c + $(CC) $(CFLAGS) base64.c + +clean: + rm *.o dual_chan_pkt_fwd + +install: + sudo cp -f ./dual_chan_pkt_fwd.service /lib/systemd/system/ + sudo systemctl enable dual_chan_pkt_fwd.service + sudo systemctl daemon-reload + sudo systemctl start dual_chan_pkt_fwd + sudo systemctl status dual_chan_pkt_fwd -l + +uninstall: + sudo systemctl stop dual_chan_pkt_fwd + sudo systemctl disable dual_chan_pkt_fwd.service + sudo rm -f /lib/systemd/system/dual_chan_pkt_fwd.service diff --git a/README.md b/README.md index 8784d96..32dc233 100644 --- a/README.md +++ b/README.md @@ -1,109 +1,203 @@ -Dual Channel LoRaWAN Gateway -============================== -This repository contains a proof-of-concept implementation of a dual -channel LoRaWAN gateway with the Raspberry Pi+ LoRa(TM) Expansion Board of -Uputronics. It is based on (fork) the Single Channel LoRaWan Gateway -by Thomas Telkamp. - -It has been tested on the Raspberry Pi platform, using a Raspberry Pi+ -LoRa(TM) Expansion Board with 2 SX1276 (HopeRF RFM95W) on board. - -Part of the source has been copied from the Semtech Packet Forwarder -(with permission). - -Maintainer: Thomas Telkamp - -The code is for testing and development purposes only, and is not meant -for production usage. - -I will continue update this code for our own purposes; Hans Boksem (hans.boksem@gmail.com) - -Features --------- -- listen on configurable frequency and spreading factor -- SF7 to SF12 -- status updates -- can forward to two servers -- Uses 2 channels (as the board has 2 RFM95's), default set to 868.1 and 868.3 frequencies -- Autostart features -- logging to logfile (default /var/log/lora_gateway) -- Uses Hat LED's for indicating INTERNET, CEO and CE1 Data activity - -Not (yet) supported: -- PACKET_PUSH_ACK processing -- SF7BW250 modulation -- FSK modulation -- downstream messages (tx) - -Preparation, setup and running ------------------------------- - -Prepare SD Card -- download raspbian jessie lite -- unpack zip -- write img file with win32DiskImager or alike - -Prepare Pi -- Insert SD card -- Start raspberry pi -- figure out the IP address (look into the routers DHCP config or attach monitor and find out there) -- login to the rasp (un/pw pi/raspberry) -- change the password (with passwd) -- run raspi-config (sudo raspi-config) -- Expand the filesystem -- Goto advanced config and select SPI -- Enable SPI interface -- Enable loading of SPI kernel module by default -- exit the raspi-config and reboot - -Install dependencies -- update the pkg database (twice command: sudo apt-get update) -- install wiringpi (sudo apt-get install wiringpi) - -Install dual_chan_pkt_fw -- Put the zip file on the pi (using winscp or alike) or use git to clone the repository (git clone https://github.com/bokse001/dual_chan_pkt_fwd) -- unzip the file -- cd into the dual_chan_pkt_fw folder -- change the main.cpp with your gateway parameters (Informal status fields; platform, email, description) and location (lat,lon,alt)) -- compile the dual_chan_pkt_fwd (make) - -Install autostart dual_chan_pkt_fw -- copy the lora_gateway.sh script to /etc/init.d (sudo cp lora_gateway.sh /etc/init.d) -- set execute permission on lora_gateway.sh (sudo chmod a+x /etc/init.d/lora_gateway.sh) -- set execute permission on lora_gw_startup.sh (sudo chmod a+x lora_gw_startup.sh) -- configure the autostart (sudo update-rc.d lora_gateway.sh defaults) - -Starting the dual_chan_pkt_fwd -- to start the dual_chan_pkt_fwd reboot or start via the commandline (/etc/init.d/lora_gateway.sh restart) -- Remember; it takes about 10 seconds to start the gateway... - -Check the log -- Logging is done within the file /var/log/lora_gateway -- Remember; it takes about 10 seconds to start the gateway... - -Troubleshooting -- When after rebooting one of the LEDs on the Hat (INTERNET) is on continuosly this means there is something wrong with the network. Fix the network, login and restart the lora_gateway - -Connections ------------ -It is a raspberry Pi hat so just pop it on an rasp 2 or 3 - -Configuration -------------- - -Defaults: - -- LoRa: SF7 at 868.1 and 868.3 Mhz -- Server: 40.114.249.243 port 1700 (The Things Network: router.eu.thethings.network) - -Edit source node (main.cpp) to change configuration (look for: "Configure these values!"). -If you want to use wifi change the following line to represent you wifi device: -strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1); - -Please set location, email and description. - -License -------- -The source files in this repository are made available under the Eclipse -Public License v1.0, except for the base64 implementation, that has been -copied from the Semtech Packet Forwader. +Dual Channel LoRaWAN Gateway +============================== +This repository contains a proof-of-concept implementation of a dual +channel LoRaWAN gateway. + +It has been tested on the Raspberry Pi platform, using a Semtech SX1272 +transceiver (HopeRF RFM92W), and SX1276 (HopeRF RFM95W). + +The code is for testing and development purposes only, and is not meant +for production usage. + +Part of the source has been copied from the Semtech Packet Forwarder +(with permission). + +Maintainer: Thomas Telkamp + +Was forked by @jlesech https://github.com/tftelkamp/single_chan_pkt_fwd to add json configuration file +then forked by @hallard https://github.com/hallard/single_chan_pkt_fwd +then forked by @bokse001 https://github.com/bokse001/dual_chan_pkt_fwd to add dual channel support, + configurable network interface and uputronics Raspberry Pi+ LoRa(TM) Expansion Board + +Added new Features +------------------ + +- Added support for [Dragino Lora HAT][2] and [LoRasPi][1] (more to come) and uputronics Raspberry Pi+ LoRa(TM) Expansion Board +- pin definition are in config file +- Removed some configuration hard coded in source file and put them into global_conf.json +- renamed main.cpp to dual_chan_pkt_fwd.cpp +- added dual_chan_pkt_fwd.service for systemd (debian jessie minimal) start +- added `make install` and `make uninstall` into Makefile to install service +- added control for On board Led's if any (uputronics board CE0 and CE1 activity, Internet and Lan sensing leds) +- added configuration of the network interface (eth0/wlan0) and sensing network interface connectivity +- added a counter for packets received since last start + +Raspberry PI pin mapping is as follow and pin number in file `global_conf.json` are WiringPi pin number (wPi colunm) + +``` +root@xxxx # gpio readall + +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+ + | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | + +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ + | | | 3.3v | | | 1 || 2 | | | 5v | | | + | 2 | 8 | SDA.1 | IN | 1 | 3 || 4 | | | 5V | | | + | 3 | 9 | SCL.1 | IN | 1 | 5 || 6 | | | 0v | | | + | 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 1 | ALT5 | TxD | 15 | 14 | + | | | 0v | | | 9 || 10 | 1 | ALT5 | RxD | 16 | 15 | + | 17 | 0 | GPIO. 0 | OUT | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 | + | 27 | 2 | GPIO. 2 | IN | 0 | 13 || 14 | | | 0v | | | + | 22 | 3 | GPIO. 3 | IN | 0 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 | + | | | 3.3v | | | 17 || 18 | 1 | IN | GPIO. 5 | 5 | 24 | + | 10 | 12 | MOSI | ALT0 | 0 | 19 || 20 | | | 0v | | | + | 9 | 13 | MISO | ALT0 | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 | + | 11 | 14 | SCLK | ALT0 | 0 | 23 || 24 | 1 | OUT | CE0 | 10 | 8 | + | | | 0v | | | 25 || 26 | 1 | OUT | CE1 | 11 | 7 | + | 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 | + | 5 | 21 | GPIO.21 | OUT | 0 | 29 || 30 | | | 0v | | | + | 6 | 22 | GPIO.22 | OUT | 0 | 31 || 32 | 1 | IN | GPIO.26 | 26 | 12 | + | 13 | 23 | GPIO.23 | OUT | 0 | 33 || 34 | | | 0v | | | + | 19 | 24 | GPIO.24 | IN | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 | + | 26 | 25 | GPIO.25 | IN | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 | + | | | 0v | | | 39 || 40 | 0 | OUT | GPIO.29 | 29 | 21 | + +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ + | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | + +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+ + +``` + +* For [Dragino RPI Lora][2] HAT +pins configuration in `global_conf.json` +``` + "pin_nss": 6, + "pin_dio0": 7, + "pin_rst": 0 +``` + +* For [LoRasPi][1] Shield +pins configuration in file `global_conf.json` + +``` + "pin_nss": 8, + "pin_dio0": 6, + "pin_rst": 3, + "pin_led1":4 +``` + +* For Uputronics Raspberry Pi+ LoRa(TM) Expansion Board +pins configuration in file `global_conf.json` + +``` + "pin_nss": 10, + "pin_dio0": 6, + "pin_nss2": 11, + "pin_dio0_2": 27, + "pin_rst": 0, + "pin_NetworkLED": 22, + "pin_InternetLED": 23, + "pin_ActivityLED_0": 21, + "pin_ActivityLED_1": 29, +``` + + +Installation +------------ + +Install dependencies as indicated in original README.md below then + +```shell +cd /home/pi +git clone https://github.com/bokse001/dual_chan_pkt_fwd +make +sudo make install +```` + +To start service, as root or sudo (should already be started at boot if you done make install and rebooted of course), stop service or look service status +```shell +systemctl start dual_chan_pkt_fwd +systemctl stop dual_chan_pkt_fwd +systemctl status dual_chan_pkt_fwd +```` + +To see gateway log in real time +```shell +sudo journalctl -f -u dual_chan_pkt_fwd +```` + +Configuration +------------- + +Defaults: + +- LoRa: SF7 at 868.1 Mhz and 868.3 Mhz +- Server: 40.114.249.243, port 1700 (The Things Network) + +Please configure the global_conf.json file with your settigns like network interface, location, email and description. + + +Pictures +-------- + +running daemon on Raspberry PI with uputronics Raspberry Pi+ LoRa(TM) Expansion Board + +Dual Channel GW TTN + + +running daemon on Raspberry PI with LoRasPI shield + +LoRasPI plugged on PI + + + +**Original README.md below** + +Features +-------- + +listen on configurable frequency and spreading factor +SF7 to SF12 +status updates +can forward to two servers +Not (yet) supported: + +PACKET_PUSH_ACK processing +SF7BW250 modulation +FSK modulation +downstream messages (tx) + +Dependencies +------------ + +SPI needs to be enabled on the Raspberry Pi (use raspi-config) +WiringPi: a GPIO access library written in C for the BCM2835 used in the Raspberry Pi. sudo apt-get install wiringpi see http://wiringpi.com +Run packet forwarder as root + +Connections +----------- + +SX127x Raspberry PI +3.3V 3.3V (header pin #1) +GND GND (pin #6) +MISO MISO (pin #21) +MOSI MOSI (pin #19) +SCK CLK (pin #23) +NSS GPIO6 (pin #22) +DIO0 GPIO7 (pin #7) +RST GPIO0 (pin #11) +Configuration + +Defaults: +--------- + +LoRa: SF7 at 868.1 Mhz +Server: 54.229.214.112, port 1700 (The Things Network: croft.thethings.girovito.nl) +Edit source node (main.cpp) to change configuration (look for: "Configure these values!"). + +Please set location, email and description. + +License +------- + +The source files in this repository are made available under the Eclipse Public License v1.0, except: + +base64 implementation, that has been copied from the Semtech Packet Forwarder; +RapidJSON, licensed under the MIT License. \ No newline at end of file diff --git a/base64.c b/base64.c index bef7165..ab17d4d 100644 --- a/base64.c +++ b/base64.c @@ -1,308 +1,308 @@ -/* - / _____) _ | | -( (____ _____ ____ _| |_ _____ ____| |__ - \____ \| ___ | (_ _) ___ |/ ___) _ \ - _____) ) ____| | | || |_| ____( (___| | | | -(______/|_____)_|_|_| \__)_____)\____)_| |_| - (C)2013 Semtech-Cycleo - -Description: - Base64 encoding & decoding library - -License: Revised BSD License, see LICENSE.TXT file include in the project -Maintainer: Sylvain Miermont -*/ - - -/* -------------------------------------------------------------------------- */ -/* --- DEPENDANCIES --------------------------------------------------------- */ - -#include -#include -#include - -#include "base64.h" - -/* -------------------------------------------------------------------------- */ -/* --- PRIVATE MACROS ------------------------------------------------------- */ - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) -#define CRIT(a) fprintf(stderr, "\nCRITICAL file:%s line:%u msg:%s\n", __FILE__, __LINE__,a);exit(EXIT_FAILURE) - -//#define DEBUG(args...) fprintf(stderr,"debug: " args) /* diagnostic message that is destined to the user */ -#define DEBUG(args...) - -/* -------------------------------------------------------------------------- */ -/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ - -/* -------------------------------------------------------------------------- */ -/* --- PRIVATE MODULE-WIDE VARIABLES ---------------------------------------- */ - -static char code_62 = '+'; /* RFC 1421 standard character for code 62 */ -static char code_63 = '/'; /* RFC 1421 standard character for code 63 */ -static char code_pad = '='; /* RFC 1421 padding character if padding */ - -/* -------------------------------------------------------------------------- */ -/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ - -/** -@brief Convert a code in the range 0-63 to an ASCII character -*/ -char code_to_char(uint8_t x); - -/** -@brief Convert an ASCII character to a code in the range 0-63 -*/ -uint8_t char_to_code(char x); - -/* -------------------------------------------------------------------------- */ -/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ - -char code_to_char(uint8_t x) { - if (x <= 25) { - return 'A' + x; - } else if ((x >= 26) && (x <= 51)) { - return 'a' + (x-26); - } else if ((x >= 52) && (x <= 61)) { - return '0' + (x-52); - } else if (x == 62) { - return code_62; - } else if (x == 63) { - return code_63; - } else { - DEBUG("ERROR: %i IS OUT OF RANGE 0-63 FOR BASE64 ENCODING\n", x); - exit(EXIT_FAILURE); - } //TODO: improve error management -} - -uint8_t char_to_code(char x) { - if ((x >= 'A') && (x <= 'Z')) { - return (uint8_t)x - (uint8_t)'A'; - } else if ((x >= 'a') && (x <= 'z')) { - return (uint8_t)x - (uint8_t)'a' + 26; - } else if ((x >= '0') && (x <= '9')) { - return (uint8_t)x - (uint8_t)'0' + 52; - } else if (x == code_62) { - return 62; - } else if (x == code_63) { - return 63; - } else { - DEBUG("ERROR: %c (0x%x) IS INVALID CHARACTER FOR BASE64 DECODING\n", x, x); - exit(EXIT_FAILURE); - } //TODO: improve error management -} - -/* -------------------------------------------------------------------------- */ -/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ - -int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len) { - int i; - int result_len; /* size of the result */ - int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */ - int last_bytes; /* number of unsigned chars <3 in the last block */ - int last_chars; /* number of characters <4 in the last block */ - uint32_t b; - - /* check input values */ - if ((out == NULL) || (in == NULL)) { - DEBUG("ERROR: NULL POINTER AS OUTPUT IN BIN_TO_B64\n"); - return -1; - } - if (size == 0) { - *out = 0; /* null string */ - return 0; - } - - /* calculate the number of base64 'blocks' */ - full_blocks = size / 3; - last_bytes = size % 3; - switch (last_bytes) { - case 0: /* no byte left to encode */ - last_chars = 0; - break; - case 1: /* 1 byte left to encode -> +2 chars */ - last_chars = 2; - break; - case 2: /* 2 bytes left to encode -> +3 chars */ - last_chars = 3; - break; - default: - CRIT("switch default that should not be possible"); - } - - /* check if output buffer is big enough */ - result_len = (4*full_blocks) + last_chars; - if (max_len < (result_len + 1)) { /* 1 char added for string terminator */ - DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN BIN_TO_B64\n"); - return -1; - } - - /* process all the full blocks */ - for (i=0; i < full_blocks; ++i) { - b = (0xFF & in[3*i] ) << 16; - b |= (0xFF & in[3*i + 1]) << 8; - b |= 0xFF & in[3*i + 2]; - out[4*i + 0] = code_to_char((b >> 18) & 0x3F); - out[4*i + 1] = code_to_char((b >> 12) & 0x3F); - out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F); - out[4*i + 3] = code_to_char( b & 0x3F); - } - - /* process the last 'partial' block and terminate string */ - i = full_blocks; - if (last_chars == 0) { - out[4*i] = 0; /* null character to terminate string */ - } else if (last_chars == 2) { - b = (0xFF & in[3*i] ) << 16; - out[4*i + 0] = code_to_char((b >> 18) & 0x3F); - out[4*i + 1] = code_to_char((b >> 12) & 0x3F); - out[4*i + 2] = 0; /* null character to terminate string */ - } else if (last_chars == 3) { - b = (0xFF & in[3*i] ) << 16; - b |= (0xFF & in[3*i + 1]) << 8; - out[4*i + 0] = code_to_char((b >> 18) & 0x3F); - out[4*i + 1] = code_to_char((b >> 12) & 0x3F); - out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F); - out[4*i + 3] = 0; /* null character to terminate string */ - } - - return result_len; -} - -int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len) { - int i; - int result_len; /* size of the result */ - int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */ - int last_chars; /* number of characters <4 in the last block */ - int last_bytes; /* number of unsigned chars <3 in the last block */ - uint32_t b; - ; - - /* check input values */ - if ((out == NULL) || (in == NULL)) { - DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n"); - return -1; - } - if (size == 0) { - return 0; - } - - /* calculate the number of base64 'blocks' */ - full_blocks = size / 4; - last_chars = size % 4; - switch (last_chars) { - case 0: /* no char left to decode */ - last_bytes = 0; - break; - case 1: /* only 1 char left is an error */ - DEBUG("ERROR: ONLY ONE CHAR LEFT IN B64_TO_BIN\n"); - return -1; - case 2: /* 2 chars left to decode -> +1 byte */ - last_bytes = 1; - break; - case 3: /* 3 chars left to decode -> +2 bytes */ - last_bytes = 2; - break; - default: - CRIT("switch default that should not be possible"); - } - - /* check if output buffer is big enough */ - result_len = (3*full_blocks) + last_bytes; - if (max_len < result_len) { - DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN B64_TO_BIN\n"); - return -1; - } - - /* process all the full blocks */ - for (i=0; i < full_blocks; ++i) { - b = (0x3F & char_to_code(in[4*i] )) << 18; - b |= (0x3F & char_to_code(in[4*i + 1])) << 12; - b |= (0x3F & char_to_code(in[4*i + 2])) << 6; - b |= 0x3F & char_to_code(in[4*i + 3]); - out[3*i + 0] = (b >> 16) & 0xFF; - out[3*i + 1] = (b >> 8 ) & 0xFF; - out[3*i + 2] = b & 0xFF; - } - - /* process the last 'partial' block */ - i = full_blocks; - if (last_bytes == 1) { - b = (0x3F & char_to_code(in[4*i] )) << 18; - b |= (0x3F & char_to_code(in[4*i + 1])) << 12; - out[3*i + 0] = (b >> 16) & 0xFF; - if (((b >> 12) & 0x0F) != 0) { - DEBUG("WARNING: last character contains unusable bits\n"); - } - } else if (last_bytes == 2) { - b = (0x3F & char_to_code(in[4*i] )) << 18; - b |= (0x3F & char_to_code(in[4*i + 1])) << 12; - b |= (0x3F & char_to_code(in[4*i + 2])) << 6; - out[3*i + 0] = (b >> 16) & 0xFF; - out[3*i + 1] = (b >> 8 ) & 0xFF; - if (((b >> 6) & 0x03) != 0) { - DEBUG("WARNING: last character contains unusable bits\n"); - } - } - - return result_len; -} - -int bin_to_b64(const uint8_t * in, int size, char * out, int max_len) { - int ret; - - ret = bin_to_b64_nopad(in, size, out, max_len); - - if (ret == -1) { - return -1; - } - switch (ret%4) { - case 0: /* nothing to do */ - return ret; - case 1: - DEBUG("ERROR: INVALID UNPADDED BASE64 STRING\n"); - return -1; - case 2: /* 2 chars in last block, must add 2 padding char */ - if (max_len > (ret + 2 + 1)) { - out[ret] = code_pad; - out[ret+1] = code_pad; - out[ret+2] = 0; - return ret+2; - } else { - DEBUG("ERROR: not enough room to add padding in bin_to_b64\n"); - return -1; - } - case 3: /* 3 chars in last block, must add 1 padding char */ - if (max_len > (ret + 1 + 1)) { - out[ret] = code_pad; - out[ret+1] = 0; - return ret+1; - } else { - DEBUG("ERROR: not enough room to add padding in bin_to_b64\n"); - return -1; - } - default: - CRIT("switch default that should not be possible"); - } -} - -int b64_to_bin(const char * in, int size, uint8_t * out, int max_len) { - if (in == NULL) { - DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n"); - return -1; - } - if ((size%4 == 0) && (size >= 4)) { /* potentially padded Base64 */ - if (in[size-2] == code_pad) { /* 2 padding char to ignore */ - return b64_to_bin_nopad(in, size-2, out, max_len); - } else if (in[size-1] == code_pad) { /* 1 padding char to ignore */ - return b64_to_bin_nopad(in, size-1, out, max_len); - } else { /* no padding to ignore */ - return b64_to_bin_nopad(in, size, out, max_len); - } - } else { /* treat as unpadded Base64 */ - return b64_to_bin_nopad(in, size, out, max_len); - } -} - - -/* --- EOF ------------------------------------------------------------------ */ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Base64 encoding & decoding library + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include +#include +#include + +#include "base64.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define CRIT(a) fprintf(stderr, "\nCRITICAL file:%s line:%u msg:%s\n", __FILE__, __LINE__,a);exit(EXIT_FAILURE) + +//#define DEBUG(args...) fprintf(stderr,"debug: " args) /* diagnostic message that is destined to the user */ +#define DEBUG(args...) + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MODULE-WIDE VARIABLES ---------------------------------------- */ + +static char code_62 = '+'; /* RFC 1421 standard character for code 62 */ +static char code_63 = '/'; /* RFC 1421 standard character for code 63 */ +static char code_pad = '='; /* RFC 1421 padding character if padding */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +/** +@brief Convert a code in the range 0-63 to an ASCII character +*/ +char code_to_char(uint8_t x); + +/** +@brief Convert an ASCII character to a code in the range 0-63 +*/ +uint8_t char_to_code(char x); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +char code_to_char(uint8_t x) { + if (x <= 25) { + return 'A' + x; + } else if ((x >= 26) && (x <= 51)) { + return 'a' + (x-26); + } else if ((x >= 52) && (x <= 61)) { + return '0' + (x-52); + } else if (x == 62) { + return code_62; + } else if (x == 63) { + return code_63; + } else { + DEBUG("ERROR: %i IS OUT OF RANGE 0-63 FOR BASE64 ENCODING\n", x); + exit(EXIT_FAILURE); + } //TODO: improve error management +} + +uint8_t char_to_code(char x) { + if ((x >= 'A') && (x <= 'Z')) { + return (uint8_t)x - (uint8_t)'A'; + } else if ((x >= 'a') && (x <= 'z')) { + return (uint8_t)x - (uint8_t)'a' + 26; + } else if ((x >= '0') && (x <= '9')) { + return (uint8_t)x - (uint8_t)'0' + 52; + } else if (x == code_62) { + return 62; + } else if (x == code_63) { + return 63; + } else { + DEBUG("ERROR: %c (0x%x) IS INVALID CHARACTER FOR BASE64 DECODING\n", x, x); + exit(EXIT_FAILURE); + } //TODO: improve error management +} + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len) { + int i; + int result_len; /* size of the result */ + int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */ + int last_bytes; /* number of unsigned chars <3 in the last block */ + int last_chars; /* number of characters <4 in the last block */ + uint32_t b; + + /* check input values */ + if ((out == NULL) || (in == NULL)) { + DEBUG("ERROR: NULL POINTER AS OUTPUT IN BIN_TO_B64\n"); + return -1; + } + if (size == 0) { + *out = 0; /* null string */ + return 0; + } + + /* calculate the number of base64 'blocks' */ + full_blocks = size / 3; + last_bytes = size % 3; + switch (last_bytes) { + case 0: /* no byte left to encode */ + last_chars = 0; + break; + case 1: /* 1 byte left to encode -> +2 chars */ + last_chars = 2; + break; + case 2: /* 2 bytes left to encode -> +3 chars */ + last_chars = 3; + break; + default: + CRIT("switch default that should not be possible"); + } + + /* check if output buffer is big enough */ + result_len = (4*full_blocks) + last_chars; + if (max_len < (result_len + 1)) { /* 1 char added for string terminator */ + DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN BIN_TO_B64\n"); + return -1; + } + + /* process all the full blocks */ + for (i=0; i < full_blocks; ++i) { + b = (0xFF & in[3*i] ) << 16; + b |= (0xFF & in[3*i + 1]) << 8; + b |= 0xFF & in[3*i + 2]; + out[4*i + 0] = code_to_char((b >> 18) & 0x3F); + out[4*i + 1] = code_to_char((b >> 12) & 0x3F); + out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F); + out[4*i + 3] = code_to_char( b & 0x3F); + } + + /* process the last 'partial' block and terminate string */ + i = full_blocks; + if (last_chars == 0) { + out[4*i] = 0; /* null character to terminate string */ + } else if (last_chars == 2) { + b = (0xFF & in[3*i] ) << 16; + out[4*i + 0] = code_to_char((b >> 18) & 0x3F); + out[4*i + 1] = code_to_char((b >> 12) & 0x3F); + out[4*i + 2] = 0; /* null character to terminate string */ + } else if (last_chars == 3) { + b = (0xFF & in[3*i] ) << 16; + b |= (0xFF & in[3*i + 1]) << 8; + out[4*i + 0] = code_to_char((b >> 18) & 0x3F); + out[4*i + 1] = code_to_char((b >> 12) & 0x3F); + out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F); + out[4*i + 3] = 0; /* null character to terminate string */ + } + + return result_len; +} + +int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len) { + int i; + int result_len; /* size of the result */ + int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */ + int last_chars; /* number of characters <4 in the last block */ + int last_bytes; /* number of unsigned chars <3 in the last block */ + uint32_t b; + ; + + /* check input values */ + if ((out == NULL) || (in == NULL)) { + DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n"); + return -1; + } + if (size == 0) { + return 0; + } + + /* calculate the number of base64 'blocks' */ + full_blocks = size / 4; + last_chars = size % 4; + switch (last_chars) { + case 0: /* no char left to decode */ + last_bytes = 0; + break; + case 1: /* only 1 char left is an error */ + DEBUG("ERROR: ONLY ONE CHAR LEFT IN B64_TO_BIN\n"); + return -1; + case 2: /* 2 chars left to decode -> +1 byte */ + last_bytes = 1; + break; + case 3: /* 3 chars left to decode -> +2 bytes */ + last_bytes = 2; + break; + default: + CRIT("switch default that should not be possible"); + } + + /* check if output buffer is big enough */ + result_len = (3*full_blocks) + last_bytes; + if (max_len < result_len) { + DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN B64_TO_BIN\n"); + return -1; + } + + /* process all the full blocks */ + for (i=0; i < full_blocks; ++i) { + b = (0x3F & char_to_code(in[4*i] )) << 18; + b |= (0x3F & char_to_code(in[4*i + 1])) << 12; + b |= (0x3F & char_to_code(in[4*i + 2])) << 6; + b |= 0x3F & char_to_code(in[4*i + 3]); + out[3*i + 0] = (b >> 16) & 0xFF; + out[3*i + 1] = (b >> 8 ) & 0xFF; + out[3*i + 2] = b & 0xFF; + } + + /* process the last 'partial' block */ + i = full_blocks; + if (last_bytes == 1) { + b = (0x3F & char_to_code(in[4*i] )) << 18; + b |= (0x3F & char_to_code(in[4*i + 1])) << 12; + out[3*i + 0] = (b >> 16) & 0xFF; + if (((b >> 12) & 0x0F) != 0) { + DEBUG("WARNING: last character contains unusable bits\n"); + } + } else if (last_bytes == 2) { + b = (0x3F & char_to_code(in[4*i] )) << 18; + b |= (0x3F & char_to_code(in[4*i + 1])) << 12; + b |= (0x3F & char_to_code(in[4*i + 2])) << 6; + out[3*i + 0] = (b >> 16) & 0xFF; + out[3*i + 1] = (b >> 8 ) & 0xFF; + if (((b >> 6) & 0x03) != 0) { + DEBUG("WARNING: last character contains unusable bits\n"); + } + } + + return result_len; +} + +int bin_to_b64(const uint8_t * in, int size, char * out, int max_len) { + int ret; + + ret = bin_to_b64_nopad(in, size, out, max_len); + + if (ret == -1) { + return -1; + } + switch (ret%4) { + case 0: /* nothing to do */ + return ret; + case 1: + DEBUG("ERROR: INVALID UNPADDED BASE64 STRING\n"); + return -1; + case 2: /* 2 chars in last block, must add 2 padding char */ + if (max_len > (ret + 2 + 1)) { + out[ret] = code_pad; + out[ret+1] = code_pad; + out[ret+2] = 0; + return ret+2; + } else { + DEBUG("ERROR: not enough room to add padding in bin_to_b64\n"); + return -1; + } + case 3: /* 3 chars in last block, must add 1 padding char */ + if (max_len > (ret + 1 + 1)) { + out[ret] = code_pad; + out[ret+1] = 0; + return ret+1; + } else { + DEBUG("ERROR: not enough room to add padding in bin_to_b64\n"); + return -1; + } + default: + CRIT("switch default that should not be possible"); + } +} + +int b64_to_bin(const char * in, int size, uint8_t * out, int max_len) { + if (in == NULL) { + DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n"); + return -1; + } + if ((size%4 == 0) && (size >= 4)) { /* potentially padded Base64 */ + if (in[size-2] == code_pad) { /* 2 padding char to ignore */ + return b64_to_bin_nopad(in, size-2, out, max_len); + } else if (in[size-1] == code_pad) { /* 1 padding char to ignore */ + return b64_to_bin_nopad(in, size-1, out, max_len); + } else { /* no padding to ignore */ + return b64_to_bin_nopad(in, size, out, max_len); + } + } else { /* treat as unpadded Base64 */ + return b64_to_bin_nopad(in, size, out, max_len); + } +} + + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/base64.h b/base64.h index c0369aa..73f0196 100644 --- a/base64.h +++ b/base64.h @@ -1,62 +1,62 @@ -/* - / _____) _ | | -( (____ _____ ____ _| |_ _____ ____| |__ - \____ \| ___ | (_ _) ___ |/ ___) _ \ - _____) ) ____| | | || |_| ____( (___| | | | -(______/|_____)_|_|_| \__)_____)\____)_| |_| - (C)2013 Semtech-Cycleo - -Description: - Base64 encoding & decoding library - -License: Revised BSD License, see LICENSE.TXT file include in the project -Maintainer: Sylvain Miermont -*/ - - -#ifndef _BASE64_H -#define _BASE64_H - -/* -------------------------------------------------------------------------- */ -/* --- DEPENDANCIES --------------------------------------------------------- */ - -#include /* C99 types */ - -/* -------------------------------------------------------------------------- */ -/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ - -/** -@brief Encode binary data in Base64 string (no padding) -@param in pointer to a table of binary data -@param size number of bytes to be encoded to base64 -@param out pointer to a string where the function will output encoded data -@param max_len max length of the out string (including null char) -@return >=0 length of the resulting string (w/o null char), -1 for error -*/ -int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len); - -/** -@brief Decode Base64 string to binary data (no padding) -@param in string containing only base64 valid characters -@param size number of characters to be decoded from base64 (w/o null char) -@param out pointer to a data buffer where the function will output decoded data -@param out_max_len usable size of the output data buffer -@return >=0 number of bytes written to the data buffer, -1 for error -*/ -int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len); - -/* === derivative functions === */ - -/** -@brief Encode binary data in Base64 string (with added padding) -*/ -int bin_to_b64(const uint8_t * in, int size, char * out, int max_len); - -/** -@brief Decode Base64 string to binary data (remove padding if necessary) -*/ -int b64_to_bin(const char * in, int size, uint8_t * out, int max_len); - -#endif - -/* --- EOF ------------------------------------------------------------------ */ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Base64 encoding & decoding library + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +#ifndef _BASE64_H +#define _BASE64_H + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include /* C99 types */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ + +/** +@brief Encode binary data in Base64 string (no padding) +@param in pointer to a table of binary data +@param size number of bytes to be encoded to base64 +@param out pointer to a string where the function will output encoded data +@param max_len max length of the out string (including null char) +@return >=0 length of the resulting string (w/o null char), -1 for error +*/ +int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len); + +/** +@brief Decode Base64 string to binary data (no padding) +@param in string containing only base64 valid characters +@param size number of characters to be decoded from base64 (w/o null char) +@param out pointer to a data buffer where the function will output decoded data +@param out_max_len usable size of the output data buffer +@return >=0 number of bytes written to the data buffer, -1 for error +*/ +int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len); + +/* === derivative functions === */ + +/** +@brief Encode binary data in Base64 string (with added padding) +*/ +int bin_to_b64(const uint8_t * in, int size, char * out, int max_len); + +/** +@brief Decode Base64 string to binary data (remove padding if necessary) +*/ +int b64_to_bin(const char * in, int size, uint8_t * out, int max_len); + +#endif + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/dual_chan_pkt_fwd.cpp b/dual_chan_pkt_fwd.cpp new file mode 100644 index 0000000..b233a52 --- /dev/null +++ b/dual_chan_pkt_fwd.cpp @@ -0,0 +1,957 @@ + /****************************************************************************** + * + * Copyright (c) 2015 Thomas Telkamp + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Changes for creating a dual channel gateway with the Raspberry Pi+ LoRa(TM) Expansion Board + * of Uputronics, see also: store.uputronics.com/index.php?route=product/product&product_id=68 + *******************************************************************************/ + +// Raspberry PI pin mapping +// Pin number in this global_conf.json are Wiring Pi number (wPi colunm) +// issue a `gpio readall` on PI command line to see mapping +// +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+ +// | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +// +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ +// | | | 3.3v | | | 1 || 2 | | | 5v | | | +// | 2 | 8 | SDA.1 | IN | 1 | 3 || 4 | | | 5V | | | +// | 3 | 9 | SCL.1 | IN | 1 | 5 || 6 | | | 0v | | | +// | 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 1 | ALT5 | TxD | 15 | 14 | +// | | | 0v | | | 9 || 10 | 1 | ALT5 | RxD | 16 | 15 | +// | 17 | 0 | GPIO. 0 | OUT | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 | +// | 27 | 2 | GPIO. 2 | IN | 0 | 13 || 14 | | | 0v | | | +// | 22 | 3 | GPIO. 3 | IN | 0 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 | +// | | | 3.3v | | | 17 || 18 | 1 | IN | GPIO. 5 | 5 | 24 | +// | 10 | 12 | MOSI | ALT0 | 0 | 19 || 20 | | | 0v | | | +// | 9 | 13 | MISO | ALT0 | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 | +// | 11 | 14 | SCLK | ALT0 | 0 | 23 || 24 | 1 | OUT | CE0 | 10 | 8 | +// | | | 0v | | | 25 || 26 | 1 | OUT | CE1 | 11 | 7 | +// | 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 | +// | 5 | 21 | GPIO.21 | OUT | 0 | 29 || 30 | | | 0v | | | +// | 6 | 22 | GPIO.22 | OUT | 0 | 31 || 32 | 1 | IN | GPIO.26 | 26 | 12 | +// | 13 | 23 | GPIO.23 | OUT | 0 | 33 || 34 | | | 0v | | | +// | 19 | 24 | GPIO.24 | IN | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 | +// | 26 | 25 | GPIO.25 | IN | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 | +// | | | 0v | | | 39 || 40 | 0 | OUT | GPIO.29 | 29 | 21 | +// +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ +// | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +// +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+ +// For Uputronics Raspberry Pi+ LoRa(TM) Expansion Board +// pins configuration in file global_conf.json +// +// +// "pin_nss": 10, +// "pin_dio0": 6, +// "pin_nss2": 11, +// "pin_dio0_2": 27, +// "pin_rst": 0, +// "pin_NetworkLED": 22, +// "pin_InternetLED": 23, +// "pin_ActivityLED_0": 21, +// "pin_ActivityLED_1": 29, +// + + +#include "base64.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static const int CHANNEL = 0; + +using namespace std; + +using namespace rapidjson; + +#define BASE64_MAX_LENGTH 341 + +static const int SPI_CHANNEL = 0; +static const int SPI_CHANNEL_2 = 1; + +bool sx1272 = true; +typedef unsigned char byte; + +struct sockaddr_in si_other; +int s; +int slen = sizeof(si_other); +struct ifreq ifr; + +uint32_t cp_nb_rx_rcv; +uint32_t cp_nb_rx_ok; +uint32_t cp_nb_rx_ok_tot; +uint32_t cp_nb_rx_bad; +uint32_t cp_nb_rx_nocrc; +uint32_t cp_up_pkt_fwd; + +typedef enum SpreadingFactors +{ + SF7 = 7, + SF8, + SF9, + SF10, + SF11, + SF12 +} SpreadingFactor_t; + +typedef struct Server +{ + string address; + uint16_t port; + bool enabled; +} Server_t; + +/******************************************************************************* + * + * Default values, configure them in global_conf.json + * + *******************************************************************************/ + +// uputronics - Raspberry connections +// Put them in global_conf.json +int ssPin = 0xff; +int dio0 = 0xff; +int ssPin_2 = 0xff; +int dio0_2 = 0xff; +int RST = 0xff; +int Led1 = 0xff; +int NetworkLED = 22; +int InternetLED = 23; +int ActivityLED_0 = 21; +int ActivityLED_1 = 29; + +// Set location in global_conf.json +float lat = 0.0; +float lon = 0.0; +int alt = 0; + +/* Informal status fields */ +char platform[24] ; /* platform definition */ +char email[40] ; /* used for contact email */ +char description[64] ; /* used for free form description */ + +// Set spreading factor (SF7 - SF12), &nd center frequency +// Overwritten by the ones set in global_conf.json +SpreadingFactor_t sf = SF7; +uint16_t bw = 125; +uint32_t freq = 868100000; // in Mhz! (868.1) +uint32_t freq_2 = 868300000; // in Mhz! (868.3) + +// Servers +vector servers; + +// internet interface +char interface[6]; // Used to set the interface to communicate to the internet either eth0 or wlan0 + +// ############################################# +// ############################################# + +#define REG_FIFO 0x00 +#define REG_FIFO_ADDR_PTR 0x0D +#define REG_FIFO_TX_BASE_AD 0x0E +#define REG_FIFO_RX_BASE_AD 0x0F +#define REG_RX_NB_BYTES 0x13 +#define REG_OPMODE 0x01 +#define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_IRQ_FLAGS 0x12 +#define REG_DIO_MAPPING_1 0x40 +#define REG_DIO_MAPPING_2 0x41 +#define REG_MODEM_CONFIG 0x1D +#define REG_MODEM_CONFIG2 0x1E +#define REG_MODEM_CONFIG3 0x26 +#define REG_SYMB_TIMEOUT_LSB 0x1F +#define REG_PKT_SNR_VALUE 0x19 +#define REG_PAYLOAD_LENGTH 0x22 +#define REG_IRQ_FLAGS_MASK 0x11 +#define REG_MAX_PAYLOAD_LENGTH 0x23 +#define REG_HOP_PERIOD 0x24 +#define REG_SYNC_WORD 0x39 +#define REG_VERSION 0x42 + +#define SX72_MODE_RX_CONTINUOS 0x85 +#define SX72_MODE_TX 0x83 +#define SX72_MODE_SLEEP 0x80 +#define SX72_MODE_STANDBY 0x81 + + +#define PAYLOAD_LENGTH 0x40 + +// LOW NOISE AMPLIFIER +#define REG_LNA 0x0C +#define LNA_MAX_GAIN 0x23 +#define LNA_OFF_GAIN 0x00 +#define LNA_LOW_GAIN 0x20 + +// CONF REG +#define REG1 0x0A +#define REG2 0x84 + +#define SX72_MC2_FSK 0x00 +#define SX72_MC2_SF7 0x70 +#define SX72_MC2_SF8 0x80 +#define SX72_MC2_SF9 0x90 +#define SX72_MC2_SF10 0xA0 +#define SX72_MC2_SF11 0xB0 +#define SX72_MC2_SF12 0xC0 + +#define SX72_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12 + +// FRF +#define REG_FRF_MSB 0x06 +#define REG_FRF_MID 0x07 +#define REG_FRF_LSB 0x08 + +#define FRF_MSB 0xD9 // 868.1 Mhz +#define FRF_MID 0x06 +#define FRF_LSB 0x66 + +#define BUFLEN 2048 //Max length of buffer + +#define PROTOCOL_VERSION 1 +#define PKT_PUSH_DATA 0 +#define PKT_PUSH_ACK 1 +#define PKT_PULL_DATA 2 + +#define PKT_PULL_RESP 3 +#define PKT_PULL_ACK 4 + +#define TX_BUFF_SIZE 2048 +#define STATUS_SIZE 1024 + +void LoadConfiguration(string filename); +void PrintConfiguration(); + +void Die(const char *s) +{ + perror(s); + exit(1); +} + +void SelectReceiver(byte CE) +{ + if (CE == 0) + { + digitalWrite(ssPin, LOW); + } else { + digitalWrite(ssPin_2, LOW); + } +} + +void UnselectReceiver(byte CE) +{ + if (CE == 0) + { + digitalWrite(ssPin, HIGH); + } else { + digitalWrite(ssPin_2, HIGH); + } +} + +uint8_t ReadRegister(uint8_t addr, byte CE) +{ + uint8_t spibuf[2]; + + SelectReceiver(CE); + spibuf[0] = addr & 0x7F; + spibuf[1] = 0x00; + wiringPiSPIDataRW(CE, spibuf, 2); + UnselectReceiver(CE); + + return spibuf[1]; +} + +void WriteRegister(uint8_t addr, uint8_t value, byte CE) +{ + uint8_t spibuf[2]; + + SelectReceiver(CE); + spibuf[0] = addr | 0x80; + spibuf[1] = value; + wiringPiSPIDataRW(CE, spibuf, 2); + + UnselectReceiver(CE); +} + +bool ReceivePkt(char* payload, uint8_t* p_length, byte CE) +{ + // clear rxDone + WriteRegister(REG_IRQ_FLAGS, 0x40, CE); + + int irqflags = ReadRegister(REG_IRQ_FLAGS, CE); + + cp_nb_rx_rcv++; + + // payload crc: 0x20 + if((irqflags & 0x20) == 0x20) { + printf("CRC error\n"); + WriteRegister(REG_IRQ_FLAGS, 0x20, CE); + return false; + + } else { + cp_nb_rx_ok++; + cp_nb_rx_ok_tot++; + + uint8_t currentAddr = ReadRegister(REG_FIFO_RX_CURRENT_ADDR, CE); + uint8_t receivedCount = ReadRegister(REG_RX_NB_BYTES, CE); + *p_length = receivedCount; + + WriteRegister(REG_FIFO_ADDR_PTR, currentAddr, CE); + + for(int i = 0; i < receivedCount; i++) { + payload[i] = ReadRegister(REG_FIFO, CE); + } + } + return true; +} + +char * PinName(int pin, char * buff) { + strcpy(buff, "unused"); + if (pin != 0xff) { + sprintf(buff, "%d", pin); + } + return buff; +} + +void SetupLoRa(byte CE) +{ + char buff[16]; + if (CE == 0) { + printf("Trying to detect module CE0 with "); + printf("NSS=%s " , PinName(ssPin, buff)); + printf("DIO0=%s " , PinName(dio0 , buff)); + printf("Reset=%s ", PinName(RST , buff)); + printf("Led1=%s\n", PinName(Led1 , buff)); + } else { + printf("Trying to detect module CE1 with "); + printf("NSS=%s " , PinName(ssPin_2, buff)); + printf("DIO0=%s " , PinName(dio0_2 , buff)); + printf("Reset=%s ", PinName(RST , buff)); + printf("Led1=%s\n", PinName(Led1 , buff)); + } + + // check basic + if (ssPin == 0xff || dio0 == 0xff) { + Die("Bad pin configuration ssPin and dio0 need at least to be defined"); + } + + + uint8_t version = ReadRegister(REG_VERSION, CE); + + if (version == 0x22) { + // sx1272 + printf("SX1272 detected, starting.\n"); + sx1272 = true; + } else { + // sx1276? + version = ReadRegister(REG_VERSION, CE); + if (version == 0x12) { + // sx1276 + if (CE == 0) + { + printf("SX1276 detected on CE0, starting.\n"); + } else { + printf("SX1276 detected on CE1, starting.\n"); + } + sx1272 = false; + } else { + printf("Transceiver version 0x%02X\n", version); + Die("Unrecognized transceiver"); + } + } + + WriteRegister(REG_OPMODE, SX72_MODE_SLEEP, CE); + + // set frequency + uint64_t frf; + if (CE == 0) + { + frf = ((uint64_t)freq << 19) / 32000000; + } else { + frf = ((uint64_t)freq_2 << 19) / 32000000; + } + WriteRegister(REG_FRF_MSB, (uint8_t)(frf >> 16), CE ); + WriteRegister(REG_FRF_MID, (uint8_t)(frf >> 8), CE ); + WriteRegister(REG_FRF_LSB, (uint8_t)(frf >> 0), CE ); + + WriteRegister(REG_SYNC_WORD, 0x34, CE); // LoRaWAN public sync word + + if (sx1272) { + if (sf == SF11 || sf == SF12) { + WriteRegister(REG_MODEM_CONFIG, 0x0B, CE); + } else { + WriteRegister(REG_MODEM_CONFIG, 0x0A, CE); + } + WriteRegister(REG_MODEM_CONFIG2, (sf << 4) | 0x04, CE); + } else { + if (sf == SF11 || sf == SF12) { + WriteRegister(REG_MODEM_CONFIG3, 0x0C, CE); + } else { + WriteRegister(REG_MODEM_CONFIG3, 0x04, CE); + } + WriteRegister(REG_MODEM_CONFIG, 0x72, CE); + WriteRegister(REG_MODEM_CONFIG2, (sf << 4) | 0x04, CE); + } + + if (sf == SF10 || sf == SF11 || sf == SF12) { + WriteRegister(REG_SYMB_TIMEOUT_LSB, 0x05, CE); + } else { + WriteRegister(REG_SYMB_TIMEOUT_LSB, 0x08, CE); + } + WriteRegister(REG_MAX_PAYLOAD_LENGTH, 0x80, CE); + WriteRegister(REG_PAYLOAD_LENGTH, PAYLOAD_LENGTH, CE); + WriteRegister(REG_HOP_PERIOD, 0xFF, CE); + WriteRegister(REG_FIFO_ADDR_PTR, ReadRegister(REG_FIFO_RX_BASE_AD, CE), CE); + + // Set Continous Receive Mode + WriteRegister(REG_LNA, LNA_MAX_GAIN, CE); // max lna gain + WriteRegister(REG_OPMODE, SX72_MODE_RX_CONTINUOS, CE); +} + +void SolveHostname(const char* p_hostname, uint16_t port, struct sockaddr_in* p_sin) +{ + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + char service[6] = { '\0' }; + snprintf(service, 6, "%hu", port); + + struct addrinfo* p_result = NULL; + + // Resolve the domain name into a list of addresses + int error = getaddrinfo(p_hostname, service, &hints, &p_result); + if (error != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error)); + exit(EXIT_FAILURE); + } + + // Loop over all returned results + for (struct addrinfo* p_rp = p_result; p_rp != NULL; p_rp = p_rp->ai_next) { + struct sockaddr_in* p_saddr = (struct sockaddr_in*)p_rp->ai_addr; + //printf("%s solved to %s\n", p_hostname, inet_ntoa(p_saddr->sin_addr)); + p_sin->sin_addr = p_saddr->sin_addr; + } + + freeaddrinfo(p_result); +} + +void SendUdp(char *msg, int length) +{ + for (vector::iterator it = servers.begin(); it != servers.end(); ++it) { + if (it->enabled) { + si_other.sin_port = htons(it->port); + + SolveHostname(it->address.c_str(), it->port, &si_other); + if (sendto(s, (char *)msg, length, 0 , (struct sockaddr *) &si_other, slen)==-1) { + Die("sendto()"); + } + } + } +} + +void SendStat() +{ + static char status_report[STATUS_SIZE]; /* status report as a JSON object */ + char stat_timestamp[24]; + + int stat_index = 0; + + digitalWrite(InternetLED, HIGH); + /* pre-fill the data buffer with fixed fields */ + status_report[0] = PROTOCOL_VERSION; + status_report[3] = PKT_PUSH_DATA; + + status_report[4] = (unsigned char)ifr.ifr_hwaddr.sa_data[0]; + status_report[5] = (unsigned char)ifr.ifr_hwaddr.sa_data[1]; + status_report[6] = (unsigned char)ifr.ifr_hwaddr.sa_data[2]; + status_report[7] = 0xFF; + status_report[8] = 0xFF; + status_report[9] = (unsigned char)ifr.ifr_hwaddr.sa_data[3]; + status_report[10] = (unsigned char)ifr.ifr_hwaddr.sa_data[4]; + status_report[11] = (unsigned char)ifr.ifr_hwaddr.sa_data[5]; + + /* start composing datagram with the header */ + uint8_t token_h = (uint8_t)rand(); /* random token */ + uint8_t token_l = (uint8_t)rand(); /* random token */ + status_report[1] = token_h; + status_report[2] = token_l; + stat_index = 12; /* 12-byte header */ + + /* get timestamp for statistics */ + time_t t = time(NULL); + strftime(stat_timestamp, sizeof stat_timestamp, "%F %T %Z", gmtime(&t)); + + // Build JSON object. + StringBuffer sb; + Writer writer(sb); + writer.StartObject(); + writer.String("stat"); + writer.StartObject(); + writer.String("time"); + writer.String(stat_timestamp); + writer.String("lati"); + writer.Double(lat); + writer.String("long"); + writer.Double(lon); + writer.String("alti"); + writer.Int(alt); + writer.String("rxnb"); + writer.Uint(cp_nb_rx_rcv); + writer.String("rxok"); + writer.Uint(cp_nb_rx_ok); + writer.String("rxfw"); + writer.Uint(cp_up_pkt_fwd); + writer.String("ackr"); + writer.Double(0); + writer.String("dwnb"); + writer.Uint(0); + writer.String("txnb"); + writer.Uint(0); + writer.String("pfrm"); + writer.String(platform); + writer.String("mail"); + writer.String(email); + writer.String("desc"); + writer.String(description); + writer.EndObject(); + writer.EndObject(); + + string json = sb.GetString(); + //printf("stat update: %s\n", json.c_str()); + printf("stat update: %s", stat_timestamp); + if (cp_nb_rx_ok_tot==0) { + printf(" no packet received yet\n"); + } else { + printf(" %u packet%sreceived\n", cp_nb_rx_ok_tot, cp_nb_rx_ok_tot>1?"s ":" "); + } + + // Build and send message. + memcpy(status_report + 12, json.c_str(), json.size()); + SendUdp(status_report, stat_index + json.size()); + digitalWrite(InternetLED, LOW); +} + +bool Receivepacket(byte CE) +{ + long int SNR; + int rssicorr, dio_port; + bool ret = false; + + if (CE == 0) + { + dio_port = dio0; + } else { + dio_port = dio0_2; + } + + if (digitalRead(dio_port) == 1) { + char message[256]; + uint8_t length = 0; + if (ReceivePkt(message, &length, CE)) { + // OK got one + ret = true; + + uint8_t value = ReadRegister(REG_PKT_SNR_VALUE, CE); + + if (CE == 0) + { + digitalWrite(ActivityLED_0, HIGH); + } else { + digitalWrite(ActivityLED_1, HIGH); + } + + if (value & 0x80) { // The SNR sign bit is 1 + // Invert and divide by 4 + value = ((~value + 1) & 0xFF) >> 2; + SNR = -value; + } else { + // Divide by 4 + SNR = ( value & 0xFF ) >> 2; + } + + rssicorr = sx1272 ? 139 : 157; + + printf("CE%i Packet RSSI: %d, ", CE, ReadRegister(0x1A, CE) - rssicorr); + printf("RSSI: %d, ", ReadRegister(0x1B,CE) - rssicorr); + printf("SNR: %li, ", SNR); + printf("Length: %hhu Message:'", length); + for (int i=0; i MAC protocol variables */ + //static uint32_t net_mac_h; /* Most Significant Nibble, network order */ + //static uint32_t net_mac_l; /* Least Significant Nibble, network order */ + + /* pre-fill the data buffer with fixed fields */ + buff_up[0] = PROTOCOL_VERSION; + buff_up[3] = PKT_PUSH_DATA; + + /* process some of the configuration variables */ + //net_mac_h = htonl((uint32_t)(0xFFFFFFFF & (lgwm>>32))); + //net_mac_l = htonl((uint32_t)(0xFFFFFFFF & lgwm )); + //*(uint32_t *)(buff_up + 4) = net_mac_h; + //*(uint32_t *)(buff_up + 8) = net_mac_l; + + buff_up[4] = (uint8_t)ifr.ifr_hwaddr.sa_data[0]; + buff_up[5] = (uint8_t)ifr.ifr_hwaddr.sa_data[1]; + buff_up[6] = (uint8_t)ifr.ifr_hwaddr.sa_data[2]; + buff_up[7] = 0xFF; + buff_up[8] = 0xFF; + buff_up[9] = (uint8_t)ifr.ifr_hwaddr.sa_data[3]; + buff_up[10] = (uint8_t)ifr.ifr_hwaddr.sa_data[4]; + buff_up[11] = (uint8_t)ifr.ifr_hwaddr.sa_data[5]; + + /* start composing datagram with the header */ + uint8_t token_h = (uint8_t)rand(); /* random token */ + uint8_t token_l = (uint8_t)rand(); /* random token */ + buff_up[1] = token_h; + buff_up[2] = token_l; + buff_index = 12; /* 12-byte header */ + + // TODO: tmst can jump is time is (re)set, not good. + struct timeval now; + gettimeofday(&now, NULL); + uint32_t tmst = (uint32_t)(now.tv_sec * 1000000 + now.tv_usec); + + // Encode payload. + char b64[BASE64_MAX_LENGTH]; + bin_to_b64((uint8_t*)message, length, b64, BASE64_MAX_LENGTH); + + // Build JSON object. + StringBuffer sb; + Writer writer(sb); + writer.StartObject(); + writer.String("rxpk"); + writer.StartArray(); + writer.StartObject(); + writer.String("tmst"); + writer.Uint(tmst); + writer.String("freq"); + if (CE == 0) { + writer.Double((double)freq / 1000000); + writer.String("chan"); + writer.Uint(0); + } else { + writer.Double((double)freq_2 / 1000000); + writer.String("chan"); + writer.Uint(1); + } + writer.String("rfch"); + writer.Uint(0); + writer.String("stat"); + writer.Uint(1); + writer.String("modu"); + writer.String("LORA"); + writer.String("datr"); + char datr[] = "SFxxBWxxx"; + snprintf(datr, strlen(datr) + 1, "SF%hhuBW%hu", sf, bw); + writer.String(datr); + writer.String("codr"); + writer.String("4/5"); + writer.String("rssi"); + writer.Int(ReadRegister(0x1A, CE) - rssicorr); + writer.String("lsnr"); + writer.Double(SNR); // %li. + writer.String("size"); + writer.Uint(length); + writer.String("data"); + writer.String(b64); + writer.EndObject(); + writer.EndArray(); + writer.EndObject(); + + string json = sb.GetString(); + printf("rxpk update: %s\n", json.c_str()); + + // Build and send message. + memcpy(buff_up + 12, json.c_str(), json.size()); + SendUdp(buff_up, buff_index + json.size()); + + fflush(stdout); + } + } + return ret; +} + +int main() +{ + struct timeval nowtime; + uint32_t lasttime; + unsigned int led0_timer,led1_timer; + + LoadConfiguration("global_conf.json"); + PrintConfiguration(); + + // Init WiringPI + wiringPiSetup() ; + pinMode(ssPin, OUTPUT); + pinMode(ssPin_2, OUTPUT); + pinMode(dio0, INPUT); + pinMode(dio0_2, INPUT); + pinMode(RST, OUTPUT); + pinMode(NetworkLED, OUTPUT); + pinMode(ActivityLED_0, OUTPUT); + pinMode(ActivityLED_1, OUTPUT); + pinMode(InternetLED, OUTPUT); + + // Init SPI + wiringPiSPISetup(SPI_CHANNEL, 500000); + wiringPiSPISetup(SPI_CHANNEL_2, 500000); + + // Setup LORA + digitalWrite(RST, HIGH); + delay(100); + digitalWrite(RST, LOW); + delay(100); + SetupLoRa(0); + SetupLoRa(1); + + // Prepare Socket connection + while ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + digitalWrite(NetworkLED, 1); + printf("No socket connection possible yet. Retrying in 10 seconds...\n"); + delay(10000); + digitalWrite(NetworkLED, 0); + } + + memset((char *) &si_other, 0, sizeof(si_other)); + si_other.sin_family = AF_INET; + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, interface, IFNAMSIZ-1); // use configured network interface eth0 or wlan0 + ioctl(s, SIOCGIFHWADDR, &ifr); + + // ID based on MAC Adddress of interface + printf( "Gateway ID: %.2x:%.2x:%.2x:ff:ff:%.2x:%.2x:%.2x\n", + (uint8_t)ifr.ifr_hwaddr.sa_data[0], + (uint8_t)ifr.ifr_hwaddr.sa_data[1], + (uint8_t)ifr.ifr_hwaddr.sa_data[2], + (uint8_t)ifr.ifr_hwaddr.sa_data[3], + (uint8_t)ifr.ifr_hwaddr.sa_data[4], + (uint8_t)ifr.ifr_hwaddr.sa_data[5] + ); + + printf("Listening at SF%i on %.6lf Mhz.\n", sf,(double)freq/1000000); + printf("Listening at SF%i on %.6lf Mhz.\n", sf,(double)freq_2/1000000); + printf("-----------------------------------\n"); + + while(1) { + + // Packet received ? + if (Receivepacket(0)) { + // Led ON + if (ActivityLED_0 != 0xff) { + digitalWrite(ActivityLED_0, 1); + } + + // start our Led blink timer, LED as been lit in Receivepacket + led0_timer=millis(); + } + if (Receivepacket(1)) { + // Led ON + if (ActivityLED_1 != 0xff) { + digitalWrite(ActivityLED_1, 1); + } + + // start our Led blink timer, LED as been lit in Receivepacket + led1_timer=millis(); + } + + gettimeofday(&nowtime, NULL); + uint32_t nowseconds = (uint32_t)(nowtime.tv_sec); + if (nowseconds - lasttime >= 30) { + lasttime = nowseconds; + SendStat(); + cp_nb_rx_rcv = 0; + cp_nb_rx_ok = 0; + cp_up_pkt_fwd = 0; + } + + // Led timer in progress ? + if (led0_timer) { + // Led timer expiration, Blink duration is 250ms + if (millis() - led0_timer >= 250) { + // Stop Led timer + led0_timer = 0; + + // Led OFF + if (ActivityLED_0 != 0xff) { + digitalWrite(ActivityLED_0, 0); + } + } + } + if (led1_timer) { + // Led timer expiration, Blink duration is 250ms + if (millis() - led1_timer >= 250) { + // Stop Led timer + led1_timer = 0; + + // Led OFF + if (ActivityLED_1 != 0xff) { + digitalWrite(ActivityLED_1, 0); + } + } + } + + + // Let some time to the OS + delay(1); + } + + return (0); +} + +void LoadConfiguration(string configurationFile) +{ + FILE* p_file = fopen(configurationFile.c_str(), "r"); + char buffer[65536]; + FileReadStream fs(p_file, buffer, sizeof(buffer)); + + Document document; + document.ParseStream(fs); + + for (Value::ConstMemberIterator fileIt = document.MemberBegin(); fileIt != document.MemberEnd(); ++fileIt) { + string objectType(fileIt->name.GetString()); + if (objectType.compare("SX127x_conf") == 0) { + const Value& sx127x_conf = fileIt->value; + if (sx127x_conf.IsObject()) { + for (Value::ConstMemberIterator confIt = sx127x_conf.MemberBegin(); confIt != sx127x_conf.MemberEnd(); ++confIt) { + string key(confIt->name.GetString()); + if (key.compare("freq") == 0) { + freq = confIt->value.GetUint(); + } else if (key.compare("freq_2") == 0) { + freq_2 = (SpreadingFactor_t)confIt->value.GetUint(); + } else if (key.compare("spread_factor") == 0) { + sf = (SpreadingFactor_t)confIt->value.GetUint(); + } else if (key.compare("pin_nss") == 0) { + ssPin = confIt->value.GetUint(); + } else if (key.compare("pin_nss_2") == 0) { + ssPin_2 = confIt->value.GetUint(); + } else if (key.compare("pin_dio0") == 0) { + dio0 = confIt->value.GetUint(); + } else if (key.compare("pin_dio0_2") == 0) { + dio0_2 = confIt->value.GetUint(); + } else if (key.compare("pin_rst") == 0) { + RST = confIt->value.GetUint(); + } else if (key.compare("pin_NetworkLED") == 0) { + NetworkLED = confIt->value.GetUint(); + } else if (key.compare("pin_InternetLED") == 0) { + InternetLED = confIt->value.GetUint(); + } else if (key.compare("pin_ActivityLED_0") == 0) { + ActivityLED_0 = confIt->value.GetUint(); + } else if (key.compare("pin_ActivityLED_1") == 0) { + ActivityLED_1 = confIt->value.GetUint(); + } + } + } + + } else if (objectType.compare("gateway_conf") == 0) { + + const Value& gateway_conf = fileIt->value; + if (gateway_conf.IsObject()) { + for (Value::ConstMemberIterator confIt = gateway_conf.MemberBegin(); confIt != gateway_conf.MemberEnd(); ++confIt) { + string memberType(confIt->name.GetString()); + if (memberType.compare("ref_latitude") == 0) { + lat = confIt->value.GetDouble(); + } else if (memberType.compare("ref_longitude") == 0) { + lon = confIt->value.GetDouble(); + } else if (memberType.compare("ref_altitude") == 0) { + alt = confIt->value.GetUint(); + + } else if (memberType.compare("name") == 0 && confIt->value.IsString()) { + string str = confIt->value.GetString(); + strcpy(platform, str.length()<=24 ? str.c_str() : "name too long"); + } else if (memberType.compare("email") == 0 && confIt->value.IsString()) { + string str = confIt->value.GetString(); + strcpy(email, str.length()<=40 ? str.c_str() : "email too long"); + } else if (memberType.compare("desc") == 0 && confIt->value.IsString()) { + string str = confIt->value.GetString(); + strcpy(description, str.length()<=64 ? str.c_str() : "description is too long"); + } else if (memberType.compare("interface") == 0 && confIt->value.IsString()) { + string str = confIt->value.GetString(); + strcpy(interface, str.length()<=6 ? str.c_str() : "interface too long"); + + } else if (memberType.compare("servers") == 0) { + const Value& serverConf = confIt->value; + if (serverConf.IsObject()) { + const Value& serverValue = serverConf; + Server_t server; + for (Value::ConstMemberIterator srvIt = serverValue.MemberBegin(); srvIt != serverValue.MemberEnd(); ++srvIt) { + string key(srvIt->name.GetString()); + if (key.compare("address") == 0 && srvIt->value.IsString()) { + server.address = srvIt->value.GetString(); + } else if (key.compare("port") == 0 && srvIt->value.IsUint()) { + server.port = srvIt->value.GetUint(); + } else if (key.compare("enabled") == 0 && srvIt->value.IsBool()) { + server.enabled = srvIt->value.GetBool(); + } + } + servers.push_back(server); + } + else if (serverConf.IsArray()) { + for (SizeType i = 0; i < serverConf.Size(); i++) { + const Value& serverValue = serverConf[i]; + Server_t server; + for (Value::ConstMemberIterator srvIt = serverValue.MemberBegin(); srvIt != serverValue.MemberEnd(); ++srvIt) { + string key(srvIt->name.GetString()); + if (key.compare("address") == 0 && srvIt->value.IsString()) { + server.address = srvIt->value.GetString(); + } else if (key.compare("port") == 0 && srvIt->value.IsUint()) { + server.port = srvIt->value.GetUint(); + } else if (key.compare("enabled") == 0 && srvIt->value.IsBool()) { + server.enabled = srvIt->value.GetBool(); + } + } + servers.push_back(server); + } + } + } + } + } + } + } +} + +void PrintConfiguration() +{ + for (vector::iterator it = servers.begin(); it != servers.end(); ++it) { + printf("server: .address = %s; .port = %hu; .enable = %d\n", it->address.c_str(), it->port, it->enabled); + } + printf("Gateway Configuration\n"); + printf(" %s (%s)\n %s\n", platform, email, description); + printf(" Latitude=%.8f\n Longitude=%.8f\n Altitude=%d\n", lat,lon,alt); + printf(" Interface: %s\n", interface); + +} diff --git a/dual_chan_pkt_fwd.service b/dual_chan_pkt_fwd.service new file mode 100644 index 0000000..a1690f4 --- /dev/null +++ b/dual_chan_pkt_fwd.service @@ -0,0 +1,14 @@ +[Unit] +Description=Lora Packet Forwarder +After=syslog.target +After=network.target + +[Service] +Type=simple +WorkingDirectory=/home/pi/dual_chan_pkt_fwd/ +ExecStart=/home/pi/dual_chan_pkt_fwd/dual_chan_pkt_fwd +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/global_conf.json b/global_conf.json new file mode 100644 index 0000000..9a7bad2 --- /dev/null +++ b/global_conf.json @@ -0,0 +1,44 @@ +{ + "SX127x_conf": + { + "freq": 868100000, + "freq_2": 868300000, + "spread_factor": 7, + "pin_nss": 10, + "pin_dio0": 6, + "pin_nss_2": 11, + "pin_dio0_2": 27, + "pin_rst": 0, + "pin_led1":4, + "pin_NetworkLED": 22, + "pin_InternetLED": 23, + "pin_ActivityLED_0": 21, + "pin_ActivityLED_1": 29 + }, + "gateway_conf": + { + "ref_latitude": 0.0, + "ref_longitude": 0.0, + "ref_altitude": 10, + + "name": "your name", + "email": "a@b.c", + "desc": "Dual channel pkt forwarder", + + "interface": "eth0", + + "servers": + [ + { + "address": "router.eu.staging.thethings.network", + "port": 1700, + "enabled": true + }, + { + "address": "router.eu.thethings.network", + "port": 1700, + "enabled": false + } + ] + } +} From 72a97a0f54f94bb17b95827eb43a6d4e942ecea6 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Thu, 27 Oct 2016 14:17:01 +0200 Subject: [PATCH 13/24] renamed to dual_chan_pkt_fwd.cpp --- main.cpp | 698 ------------------------------------------------------- 1 file changed, 698 deletions(-) delete mode 100644 main.cpp diff --git a/main.cpp b/main.cpp deleted file mode 100644 index d390a63..0000000 --- a/main.cpp +++ /dev/null @@ -1,698 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Thomas Telkamp - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * - * Changes by Hans Boksem for creating a dual channel gateway with the Raspberry - * Pi+ LoRa(TM) * Expansion Board * of Uputronics. - *******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace std; - -#include "base64.h" - -#include -#include - -typedef bool boolean; -typedef unsigned char byte; - -static const int CHANNEL = 0; - -byte currentMode = 0x81; - -char message[256]; -char b64[256]; - -bool sx1272 = true; - -byte receivedbytes; - -struct sockaddr_in si_other; -int s, slen=sizeof(si_other); -struct ifreq ifr; - -uint32_t cp_nb_rx_rcv; -uint32_t cp_nb_rx_ok; -uint32_t cp_nb_rx_bad; -uint32_t cp_nb_rx_nocrc; -uint32_t cp_up_pkt_fwd; - -enum sf_t { SF7=7, SF8, SF9, SF10, SF11, SF12 }; - -/******************************************************************************* - * - * Configure these values! - * - *******************************************************************************/ - -// SX1276 - Uputronics Raspberry Pi+ LoRa(TM) Expansion Board connections -// CE0 -int ssPin = 10; -int dio0 = 6; -// CE1 -int ssPin_2 = 11; -int dio0_2 = 27; - -int RST = 0; - -//LEDs -int NetworkLED = 22; -int InternetLED = 23; -int ActivityLED_0 = 21; -int ActivityLED_1 = 29; - -// Set spreading factor (SF7 - SF12) -sf_t sf = SF7; - -// Set center frequency -uint32_t freq = 868100000; // in Mhz! (868.1) -uint32_t freq_2 = 868300000; // in Mhz! (868.3) - -// Set location -float lat=; -float lon=; -int alt=; - -/* Informal status fields */ -static char platform[24] = ""; /* platform definition */ -static char email[40] = ""; /* used for contact email */ -static char description[64] = ">16) , CE); - writeRegister(REG_FRF_MID, (uint8_t)(frf>> 8) , CE); - writeRegister(REG_FRF_LSB, (uint8_t)(frf>> 0) , CE); - - writeRegister(REG_SYNC_WORD, 0x34, CE); // LoRaWAN public sync word - //printf("written MSB: %f\n", (double)(frf>>16)); - //printf("written MID: %f\n", (double)(frf>>8)); - //printf("written LSB: %f\n", (double)(frf>>0)); - - if (sx1272) { - if (sf == SF11 || sf == SF12) { - writeRegister(REG_MODEM_CONFIG,0x0B, CE); - } else { - writeRegister(REG_MODEM_CONFIG,0x0A, CE); - } - writeRegister(REG_MODEM_CONFIG2,(sf<<4) | 0x04, CE); - } else { - if (sf == SF11 || sf == SF12) { - writeRegister(REG_MODEM_CONFIG3,0x0C, CE); - } else { - writeRegister(REG_MODEM_CONFIG3,0x04, CE); - } - writeRegister(REG_MODEM_CONFIG,0x72, CE); - writeRegister(REG_MODEM_CONFIG2,(sf<<4) | 0x04, CE); - } - - if (sf == SF10 || sf == SF11 || sf == SF12) { - writeRegister(REG_SYMB_TIMEOUT_LSB,0x05, CE); - } else { - writeRegister(REG_SYMB_TIMEOUT_LSB,0x08, CE); - } - writeRegister(REG_MAX_PAYLOAD_LENGTH,0x80, CE); - writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH, CE); - writeRegister(REG_HOP_PERIOD,0xFF, CE); - writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_BASE_AD, CE), CE); - - // Set Continous Receive Mode - writeRegister(REG_LNA, LNA_MAX_GAIN, CE); // max lna gain - writeRegister(REG_OPMODE, SX72_MODE_RX_CONTINUOS, CE); - - //byte test = readRegister(REG_FRF_MSB, CE); - //printf("Register read: %i\n", test); - //test = readRegister(REG_FRF_MID, CE); - //printf("Register read: %i\n", test); - //test = readRegister(REG_FRF_LSB, CE); - //printf("Register read: %i\n", test); -} - -void sendudp(char *msg, int length) { - -//send the update -#ifdef SERVER1 - inet_aton(SERVER1 , &si_other.sin_addr); - - if (sendto(s, (char *)msg, length, 0 , (struct sockaddr *) &si_other, slen)==-1) - { - writelog("DIE: Could not send update...\n"); - die("sendto()"); - } -#endif - -#ifdef SERVER2 - inet_aton(SERVER2 , &si_other.sin_addr); - if (sendto(s, (char *)msg, length , 0 , (struct sockaddr *) &si_other, slen)==-1) - { - die("sendto()"); - } -#endif - -} - -void sendstat() { - - static char status_report[STATUS_SIZE]; /* status report as a JSON object */ - char stat_timestamp[24]; - time_t t; - char str[400]; - - int stat_index=0; - - digitalWrite(InternetLED, HIGH); - /* pre-fill the data buffer with fixed fields */ - status_report[0] = PROTOCOL_VERSION; - status_report[3] = PKT_PUSH_DATA; - - status_report[4] = (unsigned char)ifr.ifr_hwaddr.sa_data[0]; - status_report[5] = (unsigned char)ifr.ifr_hwaddr.sa_data[1]; - status_report[6] = (unsigned char)ifr.ifr_hwaddr.sa_data[2]; - status_report[7] = 0xFF; - status_report[8] = 0xFF; - status_report[9] = (unsigned char)ifr.ifr_hwaddr.sa_data[3]; - status_report[10] = (unsigned char)ifr.ifr_hwaddr.sa_data[4]; - status_report[11] = (unsigned char)ifr.ifr_hwaddr.sa_data[5]; - - /* start composing datagram with the header */ - uint8_t token_h = (uint8_t)rand(); /* random token */ - uint8_t token_l = (uint8_t)rand(); /* random token */ - status_report[1] = token_h; - status_report[2] = token_l; - stat_index = 12; /* 12-byte header */ - - /* get timestamp for statistics */ - t = time(NULL); - strftime(stat_timestamp, sizeof stat_timestamp, "%F %T %Z", gmtime(&t)); - - int j = snprintf((char *)(status_report + stat_index), STATUS_SIZE-stat_index, "{\"stat\":{\"time\":\"%s\",\"lati\":%.5f,\"long\":%.5f,\"alti\":%i,\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%.1f,\"dwnb\":%u,\"txnb\":%u,\"pfrm\":\"%s\",\"mail\":\"%s\",\"desc\":\"%s\"}}", stat_timestamp, lat, lon, (int)alt, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, (float)0, 0, 0,platform,email,description); - stat_index += j; - status_report[stat_index] = 0; /* add string terminator, for safety */ - - - //send the update - sendudp(status_report, stat_index); - digitalWrite(InternetLED, LOW); - - sprintf(str, "stat update: %s\n", (char *)(status_report+12)); /* DEBUG: display JSON stat */ - writelog(str); /* DEBUG: display JSON stat */ -} - -void receivepacket(byte CE) { - - long int SNR; - int rssicorr, dio_port; - char str[200]; - - if (CE == 0) - { - dio_port = dio0; - } else { - dio_port = dio0_2; - } - - if(digitalRead(dio_port) == 1) - { - if(receivePkt(message, CE)) { - byte value = readRegister(REG_PKT_SNR_VALUE, CE); - if (CE == 0) - { - digitalWrite(ActivityLED_0, HIGH); - } else { - digitalWrite(ActivityLED_1, HIGH); - } - - if( value & 0x80 ) // The SNR sign bit is 1 - { - // Invert and divide by 4 - value = ( ( ~value + 1 ) & 0xFF ) >> 2; - SNR = -value; - } - else - { - // Divide by 4 - SNR = ( value & 0xFF ) >> 2; - } - - if (sx1272) { - rssicorr = 139; - } else { - rssicorr = 157; - } - - sprintf(str,"CE%i Packet RSSI: %d, ",CE,readRegister(0x1A, CE)-rssicorr); - writelog(str); - sprintf(str,"RSSI: %d, ",readRegister(0x1B, CE)-rssicorr); - writelog(str); - sprintf(str,"SNR: %li, ",SNR); - writelog(str); - sprintf(str,"Length: %i",(int)receivedbytes); - writelog(str); - sprintf(str,"\n"); - writelog(str); - - int j; - j = bin_to_b64((uint8_t *)message, receivedbytes, (char *)(b64), 341); - //fwrite(b64, sizeof(char), j, stdout); - - char buff_up[TX_BUFF_SIZE]; /* buffer to compose the upstream packet */ - int buff_index=0; - - /* gateway <-> MAC protocol variables */ - //static uint32_t net_mac_h; /* Most Significant Nibble, network order */ - //static uint32_t net_mac_l; /* Least Significant Nibble, network order */ - - /* pre-fill the data buffer with fixed fields */ - buff_up[0] = PROTOCOL_VERSION; - buff_up[3] = PKT_PUSH_DATA; - - /* process some of the configuration variables */ - //net_mac_h = htonl((uint32_t)(0xFFFFFFFF & (lgwm>>32))); - //net_mac_l = htonl((uint32_t)(0xFFFFFFFF & lgwm )); - //*(uint32_t *)(buff_up + 4) = net_mac_h; - //*(uint32_t *)(buff_up + 8) = net_mac_l; - - buff_up[4] = (unsigned char)ifr.ifr_hwaddr.sa_data[0]; - buff_up[5] = (unsigned char)ifr.ifr_hwaddr.sa_data[1]; - buff_up[6] = (unsigned char)ifr.ifr_hwaddr.sa_data[2]; - buff_up[7] = 0xFF; - buff_up[8] = 0xFF; - buff_up[9] = (unsigned char)ifr.ifr_hwaddr.sa_data[3]; - buff_up[10] = (unsigned char)ifr.ifr_hwaddr.sa_data[4]; - buff_up[11] = (unsigned char)ifr.ifr_hwaddr.sa_data[5]; - - /* start composing datagram with the header */ - uint8_t token_h = (uint8_t)rand(); /* random token */ - uint8_t token_l = (uint8_t)rand(); /* random token */ - buff_up[1] = token_h; - buff_up[2] = token_l; - buff_index = 12; /* 12-byte header */ - - // TODO: tmst can jump is time is (re)set, not good. - struct timeval now; - gettimeofday(&now, NULL); - uint32_t tmst = (uint32_t)(now.tv_sec*1000000 + now.tv_usec); - - /* start of JSON structure */ - memcpy((void *)(buff_up + buff_index), (void *)"{\"rxpk\":[", 9); - buff_index += 9; - buff_up[buff_index] = '{'; - ++buff_index; - j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, "\"tmst\":%u", tmst); - buff_index += j; - if (CE == 0) - { - j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%.6lf", 0, 0, (double)freq/1000000); - } else { - j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%.6lf", 1, 1, (double)freq_2/1000000); - } - buff_index += j; - memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":1", 9); - buff_index += 9; - memcpy((void *)(buff_up + buff_index), (void *)",\"modu\":\"LORA\"", 14); - buff_index += 14; - /* Lora datarate & bandwidth, 16-19 useful chars */ - switch (sf) { - case SF7: - memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF7", 12); - buff_index += 12; - break; - case SF8: - memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF8", 12); - buff_index += 12; - break; - case SF9: - memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF9", 12); - buff_index += 12; - break; - case SF10: - memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF10", 13); - buff_index += 13; - break; - case SF11: - memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF11", 13); - buff_index += 13; - break; - case SF12: - memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF12", 13); - buff_index += 13; - break; - default: - memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF?", 12); - buff_index += 12; - } - memcpy((void *)(buff_up + buff_index), (void *)"BW125\"", 6); - buff_index += 6; - memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"4/5\"", 13); - buff_index += 13; - j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"lsnr\":%li", SNR); - buff_index += j; - j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"rssi\":%d,\"size\":%u", readRegister(0x1A, CE)-rssicorr, receivedbytes); - buff_index += j; - memcpy((void *)(buff_up + buff_index), (void *)",\"data\":\"", 9); - buff_index += 9; - j = bin_to_b64((uint8_t *)message, receivedbytes, (char *)(buff_up + buff_index), 341); - buff_index += j; - buff_up[buff_index] = '"'; - ++buff_index; - - /* End of packet serialization */ - buff_up[buff_index] = '}'; - ++buff_index; - buff_up[buff_index] = ']'; - ++buff_index; - /* end of JSON datagram payload */ - buff_up[buff_index] = '}'; - ++buff_index; - buff_up[buff_index] = 0; /* add string terminator, for safety */ - - //send the messages - sendudp(buff_up, buff_index); - sprintf(str,"rxpk update: %s\n", (char *)(buff_up + 12)); /* DEBUG: display JSON payload */ - writelog(str); - - fflush(stdout); - //printf("CE%i read packet\n",CE); - //byte test = readRegister(REG_FRF_MSB, CE); - //printf("Register read: %i\n", test); - //test = readRegister(REG_FRF_MID, CE); - //printf("Register read: %i\n", test); - //test = readRegister(REG_FRF_LSB, CE); - //printf("Register read: %i\n", test); - - } // received a message - - } // dio0=1 -} - -int main () { - - struct timeval nowtime; - uint32_t lasttime; - - wiringPiSetup () ; - pinMode(ssPin, OUTPUT); - pinMode(ssPin_2, OUTPUT); - pinMode(dio0, INPUT); - pinMode(dio0_2, INPUT); - pinMode(RST, OUTPUT); - pinMode(NetworkLED, OUTPUT); - pinMode(ActivityLED_0, OUTPUT); - pinMode(ActivityLED_1, OUTPUT); - pinMode(InternetLED, OUTPUT); - - char str[200]; - - - //int fd = - wiringPiSPISetup(0, 500000); - wiringPiSPISetup(1, 500000); - //cout << "Init result: " << fd << endl; - - digitalWrite(RST, HIGH); - delay(100); - digitalWrite(RST, LOW); - delay(100); - SetupLoRa(0); - SetupLoRa(1); - - if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) - { - die("socket"); - } - memset((char *) &si_other, 0, sizeof(si_other)); - si_other.sin_family = AF_INET; - si_other.sin_port = htons(PORT); - - ifr.ifr_addr.sa_family = AF_INET; - strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1); // can we rely on eth0? - ioctl(s, SIOCGIFHWADDR, &ifr); - - /* display result */ - sprintf(str, "Gateway ID: %.2x:%.2x:%.2x:ff:ff:%.2x:%.2x:%.2x\n", - (unsigned char)ifr.ifr_hwaddr.sa_data[0], - (unsigned char)ifr.ifr_hwaddr.sa_data[1], - (unsigned char)ifr.ifr_hwaddr.sa_data[2], - (unsigned char)ifr.ifr_hwaddr.sa_data[3], - (unsigned char)ifr.ifr_hwaddr.sa_data[4], - (unsigned char)ifr.ifr_hwaddr.sa_data[5]); - writelog(str); - - sprintf(str,"Listening at SF%i on %.6lf Mhz on CE0.\n", sf,(double)freq/1000000); - writelog(str); - sprintf(str,"Listening at SF%i on %.6lf Mhz on CE1.\n", sf,(double)freq_2/1000000); - writelog(str); - sprintf(str,"------------------\n"); - writelog(str); - - while(1) { - - digitalWrite(ActivityLED_0, LOW); - digitalWrite(ActivityLED_1, LOW); - receivepacket(0); - receivepacket(1); - - gettimeofday(&nowtime, NULL); - uint32_t nowseconds = (uint32_t)(nowtime.tv_sec); - if (nowseconds - lasttime >= 30) { - lasttime = nowseconds; - sendstat(); - cp_nb_rx_rcv = 0; - cp_nb_rx_ok = 0; - cp_up_pkt_fwd = 0; - } - delay(1); - } - return (0); - -} - From 4694e41166cc90036752fa45bd451288720a5c14 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Thu, 27 Oct 2016 14:17:30 +0200 Subject: [PATCH 14/24] Other deamon process --- lora_gw_startup.sh | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 lora_gw_startup.sh diff --git a/lora_gw_startup.sh b/lora_gw_startup.sh deleted file mode 100644 index 61cde54..0000000 --- a/lora_gw_startup.sh +++ /dev/null @@ -1,7 +0,0 @@ -!/bin/bash - -# lets wait a couple of seconds for the system to start and all is up -sleep 10 - -# Now start the lora gateway -/home/pi/dual_chan_pkt_fwd/dual_chan_pkt_fwd From 1a9f3316cca2b233600499c3885dfbafdfbdf036 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Thu, 27 Oct 2016 14:17:44 +0200 Subject: [PATCH 15/24] Other daemon process --- lora_gateway.sh | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 lora_gateway.sh diff --git a/lora_gateway.sh b/lora_gateway.sh deleted file mode 100644 index db8e930..0000000 --- a/lora_gateway.sh +++ /dev/null @@ -1,37 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: Lora gateway -# Required-Start: $all -# Required-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Manage my cool stuff -### END INIT INFO - -PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin - -. /lib/init/vars.sh -. /lib/lsb/init-functions -# If you need to source some other scripts, do it here - -case "$1" in - start) - log_begin_msg "Starting the lora gateway" - mv /var/log/lora_gateway /var/log/lora_gateway.bkp - /home/pi/dual_chan_pkt_fwd/lora_gw_startup.sh& - log_end_msg $? - exit 0 - ;; - stop) - log_begin_msg "Stopping the lora gateway" - - kill `ps -ef|grep dual_chan_pkt|grep -v grep| awk '{print $2}'` - - log_end_msg $? - exit 0 - ;; - *) - echo "Usage: /etc/init.d/ {start|stop}" - exit 1 - ;; -esac From 7e79cecf8bb08c768e0ab049bff888f49c9c6e15 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Thu, 27 Oct 2016 14:19:25 +0200 Subject: [PATCH 16/24] Add files via upload --- dual-channel-gw-ttn.jpg | Bin 0 -> 132430 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dual-channel-gw-ttn.jpg diff --git a/dual-channel-gw-ttn.jpg b/dual-channel-gw-ttn.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a67ac7ef3a3669f845e6eba3f349521ad27959b0 GIT binary patch literal 132430 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%0Ddh-!cLnk( zE_K79)1upgFxw*YNm)ORwrP_T!cM6%7mG12)-%%wW z%6lFaPyN!M?;6|BTGQ`0PK|P(-RyOME1B*$tatZ7zn3o7?VFeOUu~C!nrIUE&ScI- z5Kay_R&a<^6l(RtuD-WVkW4kv#xBA+ym{xdwK`}X_KiRC`?(jOzF9uKH>3V85B9u^ z?_3Hl4bXcG%YPSLwlBdk>NEIW;TMh7Z_`E<%jYk^9=6|KwuH$8ANz)?9*l;YQ?6&i zlcg9dhRc7F`FdZd`u`4o&d#nMqis}N+N+Y1(b`IwF~s~&dgoVo^*`@2FJLz*q5`h{ zfb5PYQ(pcQAwypY(h$`q>XE6c_y}kI!!Ps9y>Ay5<65<$7nyF{vD9BLMCb1`=tSHl~>sI z)d8OZAe8?b{)?{bV^vP<%Sg-TYdHS|#ECp? z{(b1iV!Ew<*nRkSUh1qq@*@dVc1{vlN#Dy&;zOT95KekY%A0qaWW-#d<>Z<&G zdeoDno?rS_<(?gXT48ew#CE*Y)HX%B*UgtI@X5VzkJLNt)z2g^Df-j7%NwVIsUKE* zUv7qPukJaMZO)n;!098bbu80VMq-}Cfm7OZ!e9^gA_;f-kz2$L;qKJFF6s%8ohfrN z8P*m@WGz#^W#b>I!Lp5D0*_b0_*6vDyZ!?a2iDW@fuDQj=Fqfb{|>+1?e%)g_V{C= zg4X?N_~p?+EGQHmq2|wWM`0e}sY79EGeHVFp{1y_nL0~i>jDI~d&b?6h zilmR+W$f1@(nmziX)N;ojZLpo-+v(7<1C>UwM_A+ADQZ0b}1Ts2P;{fY1M44ecl^N zN-dUjU^Qh4{&|c2S03Wiys3>XAaVbo_3Q1zuPLm(U@q4!nL7GMAxgn-V_>0?4;+%! zW^NFeB2H`%U4_$WRRk6v+V!H{{6*SVw=QpNaYKu>#)-@h3fi{pH!Yvcvsltp$eiA~ z;{P!7>$|KyY@WRFHbzW0PupcAeRZn4ljLIqiHuwd^z}6XcSR`vGqKodhbxa<3cknE z1d+`LTe0Dlf^_-kILCgwm%iqL6$K;CkDt9)YHR&T9(%k^ZRbl?fM`^`W%}mnN5O6D zh)__V8EFtZRT`*qwK>__oXmivUd!GtD^DB_g8KN>z-{l8X6`mCYURUjG>5rlP>7FjXnj~Q@ z75I&=@vzrdR*r}h8Nj6+@Se=e$c(FQp53Wwo5ZlYrZYjPPmEQBwVr0*9&Vora7gO6 zi1yZnIsbLj{t6h488?eQ5^fT`Q>W=snUcNcG4XwEyQLnM$>N@?MOsD^Cdew*)_Yg> zP%VBw6qK$jYOSX~v1)SYNxC}yz5INZ!PgX0iaOTY<|L!r>~dKsOV?Zr z;Qsvs7@9dDo~y85nWPWK|Ba7yBr*+{yENNGKjMiwI5eYbhU5~U9be){rIIOBly@Qf zU++CjrBc7_3Y{L8-!UG25{ijnNgEQRBc<&a@|IQ)Mel0eOQtOQ# z8lPfAvpY|IQvxQ#qxujA{OQVAeHft@*p9@=Z8(||nQ8qvkFAlNZY#w~N$FOnls|TIxG@&#ur*ewy%ZWjN1$=gAUAi=e4HF&uWAF0p zBkQT78LpwOy!V+TKg$W0Oz5>}6`ziq*u}@L-oby4Ta$u4(%P;nO9V~x8wSQ|wdMUg zL#sJbNv2cCM@g=3GC}e7t=g$E>4buPm3-$EQ6NYyOv}MY{S%fP0Ng$oWLL(WC^KhH z{Qqj9;jgOVp4=&9lP{=|8lCwaN~O75o!%$}v>%_gX_uqtW?^F4iZ z%a$aELYN?2Ejbee?C;NwRT~hIFIz;zSMzWUG`!6FI4&(If`6^Q=#AaW@R~~w@=tG- zSQ30gbtU-F&SR+GQ$yO9)xV+y=4M$&1jocEen?|f>59hEuAv1X5FfT&_LXvB1YZh8 z8qzJrhfPx9GKk!4?dhNXmJgjRV0Utwv=`h~=J@ir^iK>K@ve_GvmE>nmxi$m6{%g` z(kx}mCq=y29^nYcbCu^rR~0SWj?qR33&r;edarQDJX5ZHh#5DEJ6gC=Yhp?fNF4el zh^xDUDK%+-f19QY&|`yg`mSdLBG+ybF@Av#mHaNbFZ^)vH}V4s8YZUIR)ftCVfw#n zVuU6gRjbuy^Q{|sL z;*k^&$*h=f*FUp;T)0c=cjiXyb`w2ZXgWHZ|D+=^p4 z(g>ArKYMr`EEbjX3l`EgrxObhhL>IbUCn!6?z0pWwiiq8IkFN?_U~QYx;y@V&Fw=| zrL&8Q+Q_|WCx4%o#%Jva6ms?Qw<+seS{Yi@1=3gkxsj61WkUxL%l|Cara<<>jp|~6 zn0otJyTeZ!HHFXD2|gj;!i6b0B1ZwMkf;qpFam}6aE*!hPkw(99R6%_G@!aI2)QXE zBeUu&Gcs|k zuvU_Pj1!!Zsl+`a${T~w-Ug-ny zt%+fEQLXZ~LZEi;5>H(pg)z&3Sd5}}6Yz-zt2It~T_DthjGul9hs^e8`NvDyeOzui zeH>ZG8|^SRUuI&UQlXEj7!j_*J=}CSNeN&YwLFB{(%PL(x#ou2Ol91jX@7RVdfhWl z^|tUjB?6~bb@UV=^%fs0i+nFYrvg7xQxIrvRfNRCj03)XPZra8Q6P)m8a7mJYqhwP z>ngh%SNRu$eX>gf^YA}n_o2jnHcITZnaHKf-_GS)Hk4pU>3W`jN>2-*3LTKpYrk|= zh6hd0>=*q*zArK6iwaDvZRtcEzf#cw$bwiET8vGR*6rkkN3!HI^n3P~(%sHzHH-F` zO*lss_4CB|$A0`C`O^%l{4d!3jw&~1dPug+6Ut&$XDd;jgYDnIxJwny)}NHUt2$By z%}`t*Z@TcYx)7X#c=$vkXokKuHodH7);qX1rBzO%ru?*;<3dAX%3Fi+^0q&PS>uCa zw&ZC`HiaDclKn$m@tGpqO%JyT@`#ZqlFC}nIqy|&O`=dQF#zexmrC_#=B9j*1sC`! z1%CPe)-PNN9^s=$t`8%Y%wv||(Jg(V#7KCXHc-h@!DkzxZn0_dY+Zv^`WQE$^b&>- z;06?3JG)4&Iu}hI<*amd@@m`@g!WFu^qXTy2VnQ!To&Vw#~0|uugUwS`$E)|+sK`K z`O~iU5f4ahE$OoBQ_}L99)lQ4h93oQE6oR9|mi-+1n%xm_oFw&c%~pnsgY z-s`--qpjbu$ptf4=Iz|koYxVbU2NYtXYHG3y}&gLqTGn;{0sQb|IUxNz8k!kdnJ{} zq^Q@|p0J;_tseL)e`&Xg1Er#>YhjV8ZVjOBz<4&MHxPQK2iD+AsIBe-WDCSh%I5kt zZD&i|F`Z6fKmMSbh@;>^s_5q3{|w2!OHvuj<{m~UpL_7{zy28BmooIp`p2`idX!mC z`w_1m&|KNwjp&mdZ?d6=EyPO(NBYI-&4{?by{32p-nc}#D^sUlS;&|s!Xw}j31isI zo2K!70EKDC&w}0LQL(F92D65YQh3>{$H@ejG@tPikbE|Ct5v5frp{`^04*IkBzZsk zW}@9=crc-iIDfyu1d@ioIlLNClaL+In@Plv1bT~hptG6(l z^xg1l_ks$3u@&PI7XN=BYwL4;4k!tmGhBH_wG6)d{{2w4wg9l~v ztXAl3qRQBCDHiGkuHv{i+30qX{phJn-@yGIKQa$K)7atkRK#nNzk(-R!2qH@q%;g< z(jzc#r@Bb;)zxfxqO?1krA7cSXk;ju3s2j$=pA8&Cbv9?A8QRiN`dy?dAZ{06iO*; z+ECB!{}14TGHBgiJx>ILDY3#5a^6D`csNW3feiDQ2lE+ti#M;`$&0(1-G!NMr`fyk zTybR#h}&;^7m@$B&vU<~_6p5o61hZN12w3VL=;+;@kw-YGQ!lHg)cd1wC#-Z1OZ0P z)^Sh&Oc7R}SB=OhTyrdHuTrmP+9=T_u);E;=AMY@E7N_9#g3NET3IKUa|mK+y|zA* zEEhPa$vBUcY(qlxlkhLN`^qK9BIXh;b3TwBYN8a@bMW(fgH*)&BCgBdWOmp>NZcHy zb#@letrrb=TT_C#=t1?j!HSnAK#z0 z2RUODA|a*Cc|3}l7Ad~PmzqGi%Ci+LVou-9kI%~^q99{Mg=1zVX4wKC*0iP)M{&uP z5B+y3%GF#VEd3F@J5|<6S`JQ;?!(}h1_!DfxXCd7$2o^PG}TF1}KZ^s{JMG;baku8*gkxh>-HDVctTRh){1r|}Dw*F|!Jy-Pj7 ztANs`eQZPu)R6kCK%f{G=)Jg3VUxWC51m5br9MSe`Hm zxh;phi`}Bysy2L5Y7H455Xo<%;LBP z(NDnLE?EaZ~8KrHT${p436u4?hJistHna*y0i{Jj0D=>cc6)l zQZ`eBN9Q#TQE07=y*qCrF+1Niq4BqwcNL!(J8%+T*H@M$Uq zXF_7WyY(eZh8o+$D%vIM?7JKd?k#2hBPvy1NOAyV1KH&{HjG2-ksO#slPX3Vm?H?V zEl|F;@3GH7p>D$<<-P*hxm>wOA=Xe)K{_+iS8-<CzLx&0C2=!dYV0)E(*Gief)+$?lEE^-=qWgR<^zPkXO0 zpkxHh5I_A{``jnHSjBaP1FUvLG~Mp*6J5>oq3+Q;#WhB@)$SZq7Jb+YRsjUTkuE_4 zWQ;PT1irFd8n~OfrNI@6ctLx&ONni~)U)KwrX8YvHv)`?sIT}QTvjcqv3t2ZJrOb! zSk;*j{7!m*@VfTbitP$W@Q(*>&@%%1`aMLvK%-g>t;^ENl(vkH5i01@s zAKI2b)H0Dvg-Jebvyb|1;U6pN3(F`jqAFo;#>0F*^r6;Wo#V10-|}MUM}L-jmUfvZ%xsPq)HsSAn)5 zCBwvxMS`oE^QBZNy0lX(F-sX!B9#WLOz%TLEvZL8w*Smq9FIK-J{5dvw8YA^;4}is zy*?p&EveiZxBA@n-Jo0ZYm;Dq4&vogK3Y7G+~o=hB51bF%gm>VlI-=s>FK1Ge5lat zEqiHCBSy0{#;-Qs-+BI0!=sifT9D$!s@5j9UMq;g7n8r|T)#Ddrv+srzOLo@9nsXw z-J7bnzs-@wV9CX+N%W%rdmHpZ>O{ABy59?>#_{Q%O#?5$OWZ%w9}&dWP3_L-ehG}= zTknc}$qPboJwM>u*s;82{{4auQ1$QdF3079yVjuK*vEtTt3tD2L`j>7l%}`lrk4SH zHk?Z7wyMsbsuaA-<^JfWg1t@FX37ODZ!y#d8&rc=;}bw8z*EpaQ?2_xDqK(FL(rHM zs~oj{kb}Zm<#owd`_)Boy0V1jS_0v@&IB=_B2^1W-Jd(=(85~8i!oE!HEO@p>^q;y zcGlHzavhQWGxt~yMrzp6<7QJ9ClWYFJ&o;`)I1?Fcf;-N05^=zv%2k_sf38UTa4EV zTF@rqjOACjV0q$la26h}gyt0bhHR+Zsr&BBcfQVm&9+uN+ZbZ`8gxK(v-+>1KY2{q zTQ6%W-bnZh9ixxmeW9?nuD_H#UvCN7Hw;(0!Cht2`q}pK-NrD}Y(B`r=;p?@AyK6( z;H8KIA4@C^D_>3Za6hD2qeqM5@qz}n{!^}FV(uPNK1Qnpz-xRCP4lFRIs9oXlVFaz zG>ITTla~gzJKDAQ!X)$IU$@b>ggb=cLWshH19$}54#D^+!oig56nLS*NY<0(EX*dx zt?eepoXM!CQdO4aI{OnhdXx>>gk48IU8sT96Aanhqk&$+=%gy5SGce2R5@$=^*u?8 z%cFsbC8Q{kb)s-btiq4t8h{oH$M9ZC2ZkqohEM8Y{D%AB?i`!gwseGec~zY7h1PFmrMyigSNNW~6<_62+SQ>Yp>2UD0Bpx4rWm zMc26(tv?=IV-orAshm89zqsNHN$iW;S06ex%0o#u6`8D$&_S;(USjbr62?^5qd$Pw zyGitg`C7V*Xw}UXM@!pK2ET4fo;K&AWtd~Jym7Y{?-|RSLRl+>2^&Oj=Gu-z%gfy< z_?}o7YOlp^%K#3DKl}e88OWElP;HxYOwpT*V+VUSjGQ5Y{|9K)3w`#w#toyz?itp+ z><;hCuz6MX-cFg7kWHnGOAUUr-0Fjh`e`M4tizZ)Ids90pMDo#izj?ogEK$4S7^4F&9c*?3YuU zanN?IxRvcTKvpz&aDR;`q?zt z5Rj@~f+_Nx@T5)^x=>wau6{a3fT<1M6rSqLAffK#Z#5k5Bj6pz(SSItKbzMN50d3N z5?14jl$!Ss!r%NtanYbXm!7;vR2s{c`LFVKZB&!W=QIO?kNdxt7G?ektiAm(u$T{f zo_SWI`0>3+dV`!x(G5Wn=4wr-52U zt*#O+iM30th5KJ4`<5D%aQbEY)pGOSgAXK$C92UVP0obnqJxE;B=Po%P@#wv z8>2hQv618Dg4TnML9!w?u#$pT%d1=iba$$Hau%zE)gHj(m+Y*Ls?W0q@Gr=KYwg^; z{Z52lx^GQ97V1U#ADK-C!FiS3myb_+$!*=U5Lwc|7X+HmKnA?0yGSWx-8t91CqAbd z-%HK!8bdKZ**yAq9;!O=+m_3HL#l=!gpe+_?^_|G2`09J_!)z#FX>EuSg9@=LOyxm#0 zJ(hp_a)~mkIFtQG4;=Y0H~Q--I|X;SGB_uMXurfA#X9!bB8fpimC+bK#}?<=ch0$B zCEanwfW#s$;Yy_Sz0dK7@@A@O@c|I2PKygexXD4+)4VC-s8vV2R6v#7fRXROeif4o zu3%G*Ol?h2`nIB(PB2yGzN4yKL~owZKtN3f$od^S96b*xbWY3m@fPxFz==2O{^8&q zhjYwUGX&s^t8u10=K{iy^^kPf#XUmG3-O186J9m-DqL+eAui@^ z13&aX9PR;Rpf)04NlgY2&Yea6&vQ!&#g)H|&f^IKGwx zz!g0o4WJG;W*y#<2KLK^;AP>CQnWCao4KvxJG5$cN;^#av}?+k&NdH034npvqOv_^ zUr=76Gx?qlvjFP=7;Y!lYbY~-!vVM3X!=tirp*@PJsiH&uk1r`dc@-EpK9arxUbQ^_ zHXiY3?q9%bUfrIiL?z2E$GHe_0K%Sh1bfbwTzi{0F2psaPX?m-3;?FI7!!Sg5KAx# zj?h)?9Jzg)(6cyw19OD)9gLA2Ysww!nfUxvFH*Dx$@Jcq?oo=8RpjJ_>vL70c<7l$ zT=Bktm60y96kTGK{%M#$*a=q=i4L#PB-8n(FCZ{eqXk6{(5qQ7ZK*q^ng4((`*^a4 zB(i>PrYxrPWgf%tU#}B&+A<+X)aZQg(4vxJXC#ED%yYnbC@D@y_)QC!wysQ4BYewO zFrhV}RIKDMhdR~Xq~cK8jrcuR-f&b;9OFXTxhQ?wMB4;!zHvoXTBR`Yw6IMU#rdg<4 zUTPA>BNJ#<>}9Yn8IpD6d$oVq`hXx_T4b3BE?6Q!2IJ&~ATbeV9NZqDJVyCTx)<9a zL4_EP?H?oCFTU$wHNwMIxa+Me001dZ=YegD=tfYaOr8o!HuuEW#Q>Qf<>5!q5Wi^J zv=iO?+3F)%mU+53a|(Qeuec9DX?^>jzuLymNDstsbT4ZWNWYdLEclD-0S$ukG}}>I zMD+}*iz^rrQ-|eUvr6PSi?5INAfJ4J(-u;yL z)uLS)We!++Kxx-SUbe8}gH^5fmWAB3tyUI$wtZSxbp%qJCd9{5+(vS1YKH?aqR`H? zyc04&ORO<|CbVME(IkK6$;xFNI3BT24*&#F`W;RL2VGj;O0|cP8LsRLTz~5L$(g`j zPh}o>%S8t4ow{5)*#9bjl+h+aO+k-cuaS%6)SoFG3OZ1<0;Fqc{X`=gKywBfhkB;t z%$%{|dZI%2xHvONQ9JDG#wEt1EIt#arswMBprmw{(xQ4P`**fE*r-Dr{B1Pqz*fF% zPTy|`WJ6=Di(J;yA1%9I=!uPx|H>`&$3(l#p-UoJ@ z9VYKK$g7@eW4~v4%Cv?@YVc9tto_4B^_Rf2XCOkZe4%YYTHn2o%Qdvvj|sguK#_2o z)#A5s1M#m;L-;c!&LB?6r}PGrV2BC(p5l?|%0^L1uLp*Q>KowCEVuxAa)73&R$eZU2a^=nZ>Nl)KFZ~a8P2-L6 zrdPg=<%-c(h_>n6H8Ej}%P&|be^xl02%WF2l+qyzgPw>tv`2Z<_F4QDI{%2#10i^B zS!#(x8=_+wAF7gFzJjAkW-@yxf=QeeT|XA$Ff2DuQg)LD91)6y$|5a$bT#XGlnmS? zd!4!;x9!FAhr)c`^4`xxTvg%3<%-=IK1(M2ztTf;MkamTUzrG}`A zAjs26t2?G+2ehN>T6w=r?|gjBJ-ShDH5FpHokGrtoH(p#viVSILjVrojFU0^cgW~0 zn~6%$Tf9DjnIl9`lxsnft@7GAa*~pA)2pXf`bbF+e0Q|zco$Ax@Y0!H=z*H_Vf?*6 zH^T$yUD06ON?lNb={D%!AHz3_Hg2t#44tAhEe0r+@QY7t zT}nb27OzEX1u|V&51ti#;6>eSXG5vUzlZ$5Ij+ircmIk0+7;AU2Tx~nD)l6D1{05D3%AA$0@J{ z==YzbRJyj7ygP)B?YAowwR(r;RPU##`bd{uxfHss^Pay=Ldr=M|BW2^ZGAjjAg|Pm zQ@DPvsh#@AveGj8CZPHv)ay5AubvO3%ioN{l-cDTHt9$tpcj)y{cL6Ks%(WTCO9}? zGua4iqQ-)YYmwKNBJzl?B~Z1$qjaYo_>XL7a7>OvmZI`s93UP4;8O+jm|kIU_X_5{ zi@#OeQcv=pFNoTvVR7q}+@(5Lqn6|y;WnQ7@cfeV!(dQs^S4G#`zSdz*4~FucM?kM zW0Vn=25ztyny48qvw-Z`=ErogqR@}CrMhZ4^ih(HfMf8h|Zd<48lY&ZUgvOEWuc={g?w{rU~KfzDY zE|^)6m1dJ?&=2m&c!%&)I0>koB>(MV@Wn8ATZNBm)KWdnl>3xx|I>IgqE>Qw;v4BF zJFXLw>sm?5N=o7S?3WR=q@^Z?Q+SE{5+b4Hkrza~mA=Ke@e5`W-v;X-s%+K8^DXmk zD#-Muu<_q-{&d1+hG(uA^G@w@K{Tef%cwMb|8TwX8^92fq+8n zpRgP@>B5P^MgfU^tN#I#u613!EvdRmej8kTVB5=O_t*axw7@XX_x!QtG^Jd5&D4*U z{{fg`owK5s2-?D}7ZdX1@NI^I_Y6n6@*umBqhQl$mg5n|L8In_uII(tV&CaV)ny3h zO2mo9jEE+on`3P(lRfsXVm?zQ&U|ltWGcPLgCeviab0$R`k}9vGvyfvKP6Q5dXMm` zkGn@M3xtXU#07WZ+Uc^m+UcJ!fF^rzUCX5>w6?)UDqQam_It<4i+%X-(MGqn3$Jk(6~n7YWkl?Ydu7q&UuQd1gr1ZU!IoKxY19IbjV)7%@{ns5jqRTl}t zdco+@hybv+PD|V?GoCB+*>i@AsKOv;JdZEAlS*fNlNKYLzv# z$1$d_mPV=*_BA))u9yP#x82@CLjisEG2VO^DYAu$XgZFNbX}8;Nw{wMCUqQ3#Z=OT z7M)GZUr5*FcVb9Yj@AZ#5x)x7t@pN~gs7t&RXr3d;cEjLKbv0v(*lCfzD=9^aK zZEdiZhoRKKh?ffUe_gd`)&;ocB$gv)(e@5gD&2N@SNnPQ4vK)ja zpQ(&bUoMV^cckJaaN;{esi;fvPE=QtAMLA(1QO`|jn-!>q^8j4!HEusXzf>bjk(8E zUUYZ_&qyq@XhU{dUCnKwRaBR)`Yq389E{#m-fveoM}3Ab6pKHjr*m6U8zQx7k|Zft z<6qd6(6|Y-CQO;AV2Zw&5L2RyNXhV)hglTG!vzG^KY~N)6rX!XOb3Ka5&pU(*JCa$ zf4&bA$3x&vO6X=O1sogWUnDn55zK@OCk2#F%6^>5$dC0j~&GSoox7g!7VK%hA$S0*IRypJJ zS0D+~?)^=4oZZvN;MscSgkuZ<+Coax3sDBHRcf#eDu9Sxaz(Er9@XPxi%2}<|f6; zUMQ`B$>LAiptYSRwG>L2x*s|V2h}a{Sr{+=5AdI+L)dWte%348Pwz8wl99q&beZa_ z?=I_WaJ26z!Qa(C)N{R>s*O+2<`b?3@C)k89CORoH`WaG0y(qUTkpyLJ7Dxs$5UKC ze(C*K&?6+i>T~TvY`XM))?2Gs>2>97d;JcKB#&G~+Og75V61UCnbjK}TlnNmffKi% zdT4mn>TrJ%5D#jo+VOadTs;p743`B6#%_2kIP1ZjTMR_ox%yHWqkiH6A{l-4gHi)?wx5vH0?bJ9hDTsNv1Z&tiV+gO9t%%nZvla9GP>R zf&)%A`dM4uw*e^+n{lLkC_U6oSG5rv3O({7diz8}Mh!b>RGSMDABp-Q0W+$sUBNX9zAd3Vk8H z7gP9Ec;#*^7y*D$`;bQlVtCVKrl4M(IsF@~Nr!+N{qZIWDIOM?Uq=+`h}+Bpp4o7< z7QJrZWFdG}@n1(N6(Y#FgA*oKEzKz{F(20`<1V~9Qd>*BSr+cCd7>vw|LOZEC{;}Q zE6aA5;qat}Mv4?KYwt?zYZLLvZn`M$*a~J^oe;LP8rqgw2A8~xVUZekxTDyej5Uby7KwLh)(r{O{#s*;a`5`0caugJ_5OP}y zz<+KQ(k60nXvnmaHBuoyyXJL2_=Uhg?z;fVf3PHPD7D+F8@aWd0ep3;W|EN)i#F$s z^^+GG&6OV1#vBRB&FSC7Utv15c|cg;10Q9>pvt8zmuN=?7TDOjG}mx_ti4y2uI1Z9 z7kFEhk}GuZ_(b@1ze?7N&bJvs<{EYtU*ATi#eJx7CtF1LOC~c-m|J~~Qd#;nB5{y^GE+vyy z@sy2Frw(XOisKF3FqE~^yNYTRkTGF+dx}@)(<}#8JdKY19756NGI}{E~!cX58!A5i>LPf7JB4*+eXxy z0@FnV^-d@TY!N|Ml=Qz@o@SQBYvXC=RXP8KKc*fYdNqHD|Q*iq~ntuULzHzV`}+%ZZs;W959ER`yQtY8ciV zhEsZ!KIT1)Yv15aKhe{~B*}c80G#R}8Lx+LDtkVq)9uwbtS2E7Y|<|Bi~uKLm$<_9 zq>_h|L=sHZ_mY&)J1=ojD5ajpV6-ov*j>sS?T9k~$L#4rvi{m*O{bO^t-kYuYky(g zYBJCDYeUC$<@@$tt`;xSH|&)eL|5ay2=s!GVV1dpJdtmk|MvQMD!y@p{Ggu>;cUd) z^d2FJTAB4VRiEUw_huOfqeCB)I%Q;sOSMQcZ#Bo2lmK&0H8q~L%Eq%ZIT}GnJ>$Ex z?^V6gD)l2b7(UDb7|%QlTl>VB16Bl}T^!SmoF!_7PP>~l`ox2y$z@pBk$ zTLU5YXg$#AGd{<>R}clenQ+JH2KiNvee=uk>X~6FR1^Z13Vir(Trv z6ZYH4N2AD9vpsA|G|02O*x~3jv4rjI;7MiaiqiO;!^OWAxdfn4r<f5i{Q32%4Qz#Oq4H%|hD=+7wh-MIc#)nt_xoB3P8s#@ z-EN#T2H{T6oEl!_h{gcT(fudQ<2{8hgzm?$t}ue0^M6}PJw~z|W}xgJ{|BfwvOWwd zz+ApkT&mUo^>jng*vugmq=Gv6C0Otqk@+Tfvg4|*O?4VP&x_nKPV0_iwGPnYj^g?f z$Ef$1;oE->Nj-DW4-`r@gjCRz6`*=~!dv)q*BXNo^52YbkJ08Rm_2KmA%wHlU{IQS1Bs$__~&5NA*6DWTsFhutj*qDkhl@WMQWqqsGGc-eoLe%H)|Y zhj6Dl!E$5Wb)1vyX)N9j)RH1}aN%vVpnX!d9KEdh^Q^s%$0HKORAp`SE`$xWE1b-w zx_8C|CzoMcnzvxQwM*#TNoycIyOI)%?_fdSapJnSKFdT(m1^?u@3bC87kHyqnZ z5%_}Y@>&vZ1npxXC8>5Y00QOAymK7Yz@HpG%!5|#L!j-MJkBP8dp3UK02;~JzByfC znLea``OA720Y-td9S#oEp$@S+f>@uCVCX`M1pia_aY`Eex{AvMJ1TQi!Mi1w_ET4U zs3^PYMrW6xAQ=6kvP*HI-xDBFdAwH#Q%-ku~y!%j^K zp2L^qC@7d~y}9!|*-+9Ixg?GMzb;>|lV~*@IMbPQU6sp{T9z2##_xe7>9JHLe-=4@ zRawvYn*>1bnms`l9>=ZS#_=r2bw* z5Ea0-{ZmG@13m{HCTGM}E0er;hOM{!Md?!N(vyr$Hy3xNZ~98{au}Mpt;+d9Vzj$V zGBw&_kr3y&xp22+F6MMyW_*27j_}@DLm=n3-FY7ZL|&@U2s=Dn7oa&x;iNUFm0DWM z7q@~S0#=7p4u2;OrxQqKIoQ7)mkO5t2J<;K?nrt&DpN-NRpE>Xpqe)NP3j#c=9A0M z4u$WVq+7{Blqo^YP6E2*a| zx3&e~vZeKqyaphg^wa2B?xwMQE5YCQirTH4cnhhb2jsf45o)CfvE{f6j6J*y7baV; zl#%lQ1kK#JrR1Jpm2C5*a}J>R@v7u&3P=-&<2)~Eq7*muV2V6>L42U9CQ<@`|0Q3g4{VGY2Z+&9YXG6)42cOY z24MzT70uP$R`#vSGi0Hp)_{#R?&X*%pnA!er5C z64tdu50v1Y3ArnYGgy&lHRzy4Yy_1p{YGyiQ#)-p;2 z6k=hIHqNsP)n^3z4sweXt=-L>7Whx{aQ7uEnX0Nk2G`)werCGZ0iSfYV>dRseR}Ma z>3~I_oeD<&Txrzx3HS&8V>xTY!znr^XYo;9I)tpw-f&2(h=eL}$yCJc2Wtx5&auho zSx~!J9o`jY$IW6l4m3rs^vaZnA%kMsaqN1jpk5zFptymqF;DY{k%Nl3Koc5rP&?>ODR|UwK^~ zKB26>;j<6lw<}EVOPvA#ZlOdOPbgw;80l<6_puoMtR4RMfb)o}YGc7bnsr$AJ5aHk zzHN`ty%Q+LVkXIgxKw=8lj6GKW4ZaO@%BH|aP1);;_Zoza=u{-d+B=Dc5JRdZh{)A zysl8&&TSLJiJ+Zup7y=@lwW0dnk{PbUOkn9gSQ@p*qNJ;kHsUb;*@5@>!1V><=j&X zY2}XAg5~ zO$A@Hm8j$dRD^8YD32FW+ROszG=sUsJO{$L)jA`d?j3|l32r6u$u!_7v|1|nON7e( zb-8bc7Cz+fPK4$x5jPKxuO$aJwgR6q{9fouiTm$~g_A$Q>fERg@?-|OvNRmXIzc9| zT+!B8;44`SxRqG946s^Xm$(dHLs~4P58A=H?3F>}`&o}dQ32CuKCKE|;J9q~``)@j zz3Ersq)BYl`?Y^7ly6PxKHz45AMtIVeb3UO+>v&0lM}F&#pC33NFW=RUh)gXly<~E zT6s&naf7a50l(?&TDm662Nm$TBFG-aW5Sj` zwqjX|o`^mk`GNnJNZLwL{3kYKZVZYfwWoE+n-APm5&IT5+rotZDVbizgbGA+hNH2v z?VVLkZlF1-LKpK!IAfg1{} zm%PXb&oNzNYiUQGh=o1KfMqAS_Vx%Ex*LFD(Km*{`k595_HV zn<3bIBcih8FO8Pfz)+h3=|s6SBe@F&aZ&l|0q?RGdxnG0rQ*Crq}Z~b^fzRKpJ^Y+ z5H#=?QWxZ|L33{y|Kp|S2(QKkhb~ILdR)j0`Z_e^-3VR)68(`a_7s|>TV@p(`>9)+ zrkF(1)R3>qFWYGeS>{ni{QkD=g3Oew{59XH=8t05|1fZ@l zcG>&GNj}%mLqt<#5h2&yPum8ZLO}=V1;m%#DYpmc`p;N1#g|2A$~iEO!I(VV#a`Sh zM&Fz3Lb7c>Rdu-<_zhQ|ey!18Y%xQ{N=!4o)jg6ns$Zp+JvWRxG!l~j=cnOou2J{f zhiW3l1>|^^p!H|dtiF-u&@=>4@HSS&`tyt;KVPRlUlO;JJa{_!#z59s_e*_kY#eb( zK<}x}$J`GtTp8!21m)I#jOazO*YpeF!cddGnL&`%SHkr$DDk4I=um#rkR1 z`k9B)nTT8pj$2=+Br%C5yAp=jj}eE~W;bcpRivfI##vZxKq7@}kquvj5mS{@K8V#B zzvC%=z>3}sUH3?4VWZFp)PlOxI(~=Mc}O_zTngE|#wAH3D(-K(i@x|k7ot4pXBKd( z)e?St=AKU1J)50|R+qP&(E3#Qn)cFps#xf4@!ao}Z6-`j*Q1K#VED%Gl;7sE1klOP zFS5_ZN1*fZBwvT*tFJLJffrw~zV65hKI5zBTiT*a(KQVDpZb^VD`;h2^@)Bl9@m)} z-n?_*>I+X7Js>!8CmuIq$=ZswtPdrU4$l+B$pTpN6)w5^vP9{cx&+o!X$a{qx!lr4 zTv*2CU`5LDD?r$??uR{J9$-5kWo`JzUaT36FXb;rYhu9fqw^9H1s+ z&yM5O?~O!WB0L^nL-7pDaF`2rIn#0yQug@G%l9koI0Z$eMEmmlQNebgH$2^Kbkl{9 z!HCYQlGO#Yo+2rh@`ShWb@PDv9n$-g)(s}?+r95L|;qwEM9w~EuT`@X(+LCj=WN7<%L_9BZ1RE4Lky)>gj|Lmz3!WOF zK#l-!q4;!Lb{Y89xHx|@vAC|v9d`iyP%}*n@XadTj*d~rm~reJ+H_duw=H5|G|_{W)xRA zis|-9BxC=VUoGS4O+1fnVZwXC2&N|K5jRWLv)G+89%fHp{X_lh{HaEKRtBx z{xPAi2jg;U;ybcR3fC;Q-Kg|n_!-bEm#`!f_&|rr5SzqFHx~4_ z%8m~|eGl^UoC18(7`)w7ysL8MxWc+MZ8fOlH_M|)FeJn5of|I1y}OdDPiq%rlD7Z@ zJ+Ax+ePJ#GV5{D8EO`i1jYX@`yhBdL>lRs*OXNz2+3>wq_~&)8yheJln~1I4G8$*L z?cP>-q%QB4|4?0Nft)VOHMn(BXMJe`%Mch7aD45Be}` zvKUB5E$1b`_^|+~N|kK;S`XYzbwk*vt2TNg*RYdtk_riUre+8Bv+@E2t85@Hc&(;?24}J#c%x!X1iWP!lS3~QtYa9%8i<%%61NL8(HvN>gGi$?>-(<^}9MSWzzJEaF( zNJ;|4Bhy-W@!xi+rBKf38q42F&OVP%kCv)!B87fg!^fiBvM+xzhdac1&eDbNC8H&2 zY(z>|?hFU$us)Vu8_nnGE73l!K~+~ zK<5K+N+XZ}=&7Bz3B4ltHEAD}bJQ!LM&-E+bC2;jCR)y4KWVoYb@_ME$>9xW3G~JjCYbNeV>No- zeEweUc-iA|Z*FH-ZVAoQ-3K};g~=o%m?+G0EjZ%;XcoXwkUea}xsp zg`Zr>q!rKohicDnNPQApibipA+R}chweS812{)eOv?w9O{Fa?DJ8xmx#W;TjuIxuG zr8DKtg>}WmEA?qDq^?g9^Xq6<0KkRZtF~biPe8cjSgXg?wb7^)rmlrK@xGGiDe>@Y zhk}*nYGeJkKDp9cWJs}|K20K(<-fm|!ld6_6iZo~TOUUa%b@7MCSqk1pq8eV(3V=I{T{?a~mJ^V?e9-g-Rf?cD!e z0FK^PeeV9xI3Yb4{H?Aaz+^`4mDY=Bxm^LqQb^QN^C8ujHYAmh?-BjolP@WaIXOf2 z662Hr-p><#)k?K%S?2V8Da%=7je1an16t%93)V2c^#Ld-d)KO$uBOVG>i zivS&_J+Hj_4@G%Js}(N{WJO1CTh=*eX8mbcolFOr>{XiuW$+iY3zjNYF-d7s zLl@~qGiNt44F=ls|1fokTZJPVqSzZ%bR?wIsPYvwxsGu!yg9HFXFKh zyB*!Ae0-uM_QK)GxLfOSrL4baMlUN%s3oHn#5Za8D{JqMW}1d*=DVO$rf&rKNFQx4 zKzV2?It#CV%6O!@hklkPog+v8R$stnom%n zvzAmWefx`IVMSkcU*d_{f4tFC19!pfxqP~g>`nxXurZVgX|0@`K1SKiqAts}{764F zhcUi*xeY*hh+G#2L7X#A#K z;&H{UHaWPKQxS=fSUH=j5?o&7gqltl=K;Mppr#)rBTNNorpD=95HJ;M8%#zRf5x|R z=PTyYE&z39rTCf3CvvP+vv3A787zz~z8(^FT#4oMIV@B0b!YvP9M#0(?nf@`GY1#* z?z`cACIv$?!*-BGp)NN*5~br(@Zu!(YT8s@k9f7&Y=46-6P~ekY>!zSe2P;In%MOolUIC7)dqfhXYAc;uOIQD5pi8bPT9d%>J9v+eNILO z3o$+ge#FJw=RS$bwPuK#h~=NB@QFV!R_TGQ6SoxZCp-@77+Jjs6QO?hTnU+J(e_mpi7PQyW2SQfoQxP)K4csE4 z6WkbWcIU*&qNru^?eH(#54y8F>p&)n+Je-?QKSwALB0fy^L6lkFu^S_ID~6V@T|)ItJ@#({ZN zx-ymaebskhP&G-OfSDgl7hVg4ZOc{1h#<~e3}s$pIGIWN0JsOBOrw03fB`?$9Xa4k z)?vP*^?^zdY+0F3kF;}b2l6F@i8fsf{MYr(B>#60tSgJ#U^zrdXLIg`8fb5$@VGiH26My*;u;n1OvD;5O z3A&a1;y^@XieME-M_ET%O^7Kc=*jK7)#6nX6rz&>tz1bwYhdZuSv@2B9K`DD^V+ww zr3Nc;_;dSdV^?Xv0Z~0!lvm|yXC*=_ZI$5~GSB^O_+Mr(#u8|6QnjqNiPu_Hca5}j z-7&AQpFi8c*(6ANp2a`2Fg81AImIeEBy{TQ&w6QrwyF#Vv4yGi=o}2DTM|+KoZ8Pr zEtGp)R>-&ko%tO2jQJ*PNT4VLfE!ag!=bF_u80&)9ks{+QZX)qkT94ywnVo62LQJ% zH)rxfp0YA#YItUqhXn zdDm%LYdK;mDc4*mNAIRiVy~Ngk^o41hghI?it@-Fdy5&G&x1&CmUhU!l(p0pYq*6I zi5Pql`$Mn4lCi|P%C(foW5;x?L+@*U|A-E@OhWtt3S|WE_ssl@3KrI6zldVa@HA2$ zM;0f3XBz~?Y;KBRcWKnstjPQEONDQH79~d{(WOVk4l`^!aG;SuL;Pm zh`T=ItX_q3yo5|blBQOH>xFdI-jK(ol{K2&++i@WDfz^=wSw$EUV1n(Co}{o5j9n! zuSF#Uy(!Q3J9MxE(t@3M)`#V+6C!jv80Rm%v5R%pNv+uZ44AH6i{REh4JP^hOK5a} zox#V?8yJ_I?F(E26zH>k>Ia$V=*(V8;#d%mR~>s11b|GXpoLan36dh>gBV%1V2RI^ z3?PpfzUA0b_L{!f@7Z-KBFb9v^%q0Q9uCm~3&_TOy#vP_K0N0jn^8u3Cjmrt#{@kT zlWpO`L%Fqh6Jx!xaJ7R_ULPE_FwEXSVtw2;6V#Q6Yt}ukhztul{RyhRteO7rQvt+0 zba+C9dJhxgswb^xEhXgA>?_{17q;}%ZN?;O$dX2qV#dou_v zKs?#XiruAFmP_Oj?dqc=b@@2Y#6E$^P~p)0Nzo>r_q-6A+p2v_);jj~5lbzdCuNGm zO#WSm6}wk!Tct@R6K1eecfVq&F-Ya6E~J=nC6j&LjaPzyXlytHs)Q70r_Q*&_+T$n zxJ0~DXs6mCm#~W5C{soIIh?U&EGi&V5wN~#4JVq7+0^La2pq3+)J6I^^z@)+i@)-L zo3@tH-%e2(uq4jV15Q~fr6*`j2lE3T?HMz)Pg9`cTIbnU#(G65JEQPqvpg3we(7of zz<_(E*$=+({bZO8u+N%d8Tpg$KI>Z=hX_%t)X+^^dTNoW)O#K)EB-<8ErGg2K1slS z#-HC9_^%uzWw9>{l{l`4Og2$ok}n`0AAfo}@_8_1E5_Ws{*nPnbkUHQYmXKJypmwW zOC0m@oL+6_MpR9-WC~LqS)PjYC^~UhKp_m*MMG8RNzT<^?Q#rQ$P&VctuNttwH0+(*x?;}=~rfsvTXVdoOAxsBl zGP7!c|G>uQKdfpt+L*6rG(!-}s44;_L_+@`#ef z;=Ls0JsAHtgkUJEk@^uP)g&phFt7-Q5vW?61ryFpiZvi3K00SxEy8-EM z@+8)$9U>i^Ru`BKd23%DDDf`6aQ5H?A$U`D*52rvKh!&S7`n?N%K*4U-|qD5iR|ao z_M;5--er^HQ-}XIHZzQ`*o9A-2O7&L-v}Z1XuCg z3abMR>uwmm+)i-D^DwHTLSQ@KvSLQxkZl{oG4qs$Dwr+p2<8d|!7Z!TB5I^(>KWl&p_AD2^G92r;C z+wcwf^vJH3E#7h<0dQ3zzyAtX#?4vWP`E`h?DP%@OW%D&qb>+Iq(!Zm=I2lsyWa>( zOpE7sf`YyRTfLHE-cPweg+V|o1q{Ok5kfumT>+@LcQSZlZJ7w*DU_^0fE&FAL{08| z=?whv2R3B*RbW?8?T>;&JGts!fxEhmj?2oP}ds55IH z1x!=f7}0z+ZHO75hl&6CY>X?}l&$4Kb-BJ%$d9>S2V>e~{&(MMFgNZuh3#a8MJa7y zYVB^MW^c$zH|T7l?959qBwz9TG%aj%J98ps_EJagZnDN!c;!v%;q7{~6xL{-mh4#7 zU#{z*&7oBt7M}mrMOM3Cz#dL!Jg!*6y;|T3&U@YwMVI-wq^~Zq|qR9P8GZH5C&$PTT*FPz3saDNYwYECmLQj@G6ZdZ?v;M6qkH`2SPBkekYqnFlV~zHpt{Y|VSe=?WwL(-&eD zxi7iow3wBD3cg{p!0i4-;U|B^uFhS&a?-=}e>4xy)(@wM*!$$5y1wS!qjRU&>bw_9a{hc5KxU4>ej6o#wKsbO zJYF^J-|)j;GgsS}9aV-~js8_II-7FbPyG8>RkOoc6)zGJvk}Fb*ppZ(v!?gEI$K;> zyVp|bA+6M}u_$ks{wg4pp|uQ47MZYs_W`TFaEZ8St&`R5`(^)fV`HkOLd;M>3Y3L+ zp)7UESTog`LN8Na8g+z-x!s&=YsIhC`nr&CpNKY4rDf&S`f6N(+n3@gi-p?U&GDIwDNO6U~T-?bAmV z)i^r&LXK+?l8GxzCCplV$_ORdy#=UJcvo;=gZ>zk3?y?yj;?+kQz=$aoomX)JQ(qI z*&@fT)Hj6Mn2j!PbvW+|H6e?Q5slF9%;X@e=)5k8Lq5M5b;C~Win2d4j4BwL8^$Np zrd_z9WFR4F1XkhiIOkv>6!}VfuBQkS|6Nd*=mDlp+xxhthfdz$48&*bS#XHP8;!RA zs+GfOJ!ZvTskV7kn%0h<>@V^c`}o!N{v4Tzb~hQ(_ga<16=45yS!fQ3+p;O!(j0W? z0B)4Tj^vz8h2onWjM75wPm`6Z4^VdRwPs5BjQGVhhku;eR+YEdJ!upP&4#p&AVdb^ za4;UWfuEq){(>t6G+|CcMrOaNdl|h+EsPH3oBmHp?{~m5(@?Xz`A%RD9fSVV`ngd? z_LgZ|;>Xcv>9VYRVT@Hqg5rD1d>C0~(p(tZ6(in(W&u9leRwCJlA#0s*UG=#6jIX; zJP80^;c3>X4RG!<(1uJ70S6?Q@q_KLeYr`F5J9bqK1UTeRqmO(P3vtZ_5L7TYty>Z z>bs0VM6vx#NF8ZGgX84=j^#l#jdeeZneu9of*!1Du@A`2O_|8xTKT7#rQoS8BOOOm zGA(e=J$h0m<6+n$a!o?gn#~wWkvM(M`!(+RjO~iKK$uSQ&58ccTuWJ8+~RUK?3Y~p z@4Q;AfM31=J@ZCG|D_({u zBGg!2iNlUnHle!0gk*KCI3CWIKUTuPBvC3{*`a`F`K8q zb4LYx<8euDhR?1))lN_ncN6@%OE6<#7o##9M(b%lQTmYae*lTG4qc5!roh5n-fhsY zeh5A}ac@ScWxckVc}R%L56p6MVco<^UesPqy|(ad;f-|vn0pZ>QDIwu4X`|pik+o7 zsh3#RvL}wzKWBMKbK?Dnl-%%qJ*=qx|bLy=+6DuNC{2BqPm)zIe#t*nydA@5O6Fw@ z8ySkX>>2@n)0&Y`z1bRl#0r;hxx<*R$V5%Mo_|F624>=3pCne~p;SU2@>yUUXX^T8 zRbH{oUhX(^y=U&bF{l}X=LpmkFuJraUM`QbinDJNXgw}>(=J>0>-rr<{0L787orFp z>1LwVGNwa2rVCp;OG9{Fdj&7I{)u9IcaHC~{F`*}g|Mcp+Td05cl)$Q(aJeudKWg@ z;Bl1U7m+eqx1ZwQUJkln?6jPtl6abdwG5kZfV=M9209D>d=-^Np-_0jLs801{tK=F zpuJdSCX8>mKId{`C2FbWvj|=_1b#fC>w$+JY+2B(S0&pZ6LR5*u?mAyBq z{kG}MB)~ZBd#ik?^xTQMu($4EB@5I1XR#|a<+0b#2GAO+8n$=-h8tA_`{Z3FMu9H_ z3p*X2D%+g3w3L(hz-VBnnYUxk1iEv6Kh1EV(Dz=kB15_M9aj*6Q5g9|yAz6Fc4a;fg#@Y#hDxyIB`qivcO z70S&r-{M|Id-ICso(`N;JZoQ~?N!dyi33y3VA4*RxSwi2Wl5)<*DR>lv|)hWdpp`{ z3CcAJ#x&dZqBsLSz}ZbcBf8AszXcUh>2-MKz;uGXRi@o+C+}*e3{^F8MFK)#@#oXJ^Riq{rH$6a z{TN_(nY#LX4}zlpy^pVtZ=X@}Td9m9VX82pU_#AK%xGi->g=d)wJkHBq9ozwXFBkh z8i?-_4q~KH-ZiyCPG6At^*YNrbz$%Gd48jVlr#q2ed0xb_w}1uUlitos!sElkI1_9 zpH~N{RN9Hdj8YW?S>0v-XP=TDw0m|1I2yANu!K4-Fy!5vR`466b)pF*iF~`cmT+;Z zd%cwT_mwrq+W%vEyph^$ z^5xEq`>=zZHoZ;VQir0T9XzX=p5Zlp+onySrqsQ!IPFYXUQG8VW66`aCP@bTT-Ol*t;34BP+o z?R~a$&xBgpC^Ze$Q*V=1JInoa3~}QhsE(lJn?4e)I``?5nyD}2ojk!LvLN${6dkfR z+`28NfbAE;q>!VYB4#%q5@TwrcK*o3R(0AQ8P=*7Bild64@h1Qws!*!h03r@6q?j4 z&CM}KTkzfk*yoY~k}D6&!`>Ku$+EV&V`24~@zl(%1J3&1f>V+IThZ~&%IuvfBB+N` zbbps6q1uAUz(Bhk?jTOr*}pTQ!`M1^dJziu4G+5Qp0nJj=fPFyN1Ilz=ga&AnQ5I@ z7Lc^)Ul9Phu5&+kwv&p@;1ZXU;a|*tDu48L^kyu}|98I!nL4NN(R)4E)9H)hDk|KL z@G9C)gjPVSjK+F#Q1!2!I}9R1(2G|Gss@dL3Lu}9=Q@F~!32Sl!1(o_#K(3)5ISbP zP=vx4kA0SCyWFeUw#8a%1LEKv-A}7VbNAeVnMl3%&UCr*gK>SjXqVT6J*5E>J(q3{Hz^IVaKzWpN55rS$juyG45V3wMm5-(=Kp8}>R# z5Kz+Nva>Lewz?0>on`aasgd*JGU)W|r!-8^qCteAcFCfYyW;jyHJgN7p%Ui3(GM}# zw|c!g+r2n$ebr4E-zotTNKm!XOa{!3xN30H)4fpFo$Vp-ljy59%zT=8T2E2&XZE_) zN#bkvX|CfgD8d^CKBX+Wu0Gio=h9%G@YYxKZ(jNONIq9XXxo9_(U;xa8KC7s)N(J( zs0+*hD^D#L2Sn^|!=B4X=lXXpa-)F;1dbNcaA-yNGh%R_WS^Y-f2vl-VY~%Za{Xea zT04mR_2jofB27GLQ4t`s_!qy50?v*qPB{~FUg>{z^^cirA-(v>Xy3L_fNk6F`_yJg z?G6NFg)*sR=I6$C=`LO3D+}V=B1)u#z$u&I~@*q3C&= zI*lUlGQQ0iT#md~jgY4Zu3y{5OkN3XA%Evex;~(Vk^m6~Y*2N{N8X`$$})_HkvBw0E{OgVont>j5KHFWE0mLDv`+T{PcU zr5W0O&@#{|8qKo$`$njK;G!0p$iJzuKZ(FCWS6JJjICQo0)7GH7s||%>{nj|UXc$8+$xdaCf9VAvxms8x{D?g9iyxb(-%`dYs}0H6G}55-AaYJ#JDYrd z{A|^o*(Ze?1bJEHZEO8fh2{gSDw^5<2blKC(;i|CAZzY39zsPMs@{caz^Ok>ZqH3G z%59z?GQSHc{&F9IGS+Fj)s$AFy_Rf{jeFWxiS{xa@~f`9^&>^o|62hzxUiFlJ0yB3 zy=-4|Ow!@F@|h$csPm9dixJD@#i_Fs)#Aq9UGim6@a5$lEJy z+f6UtHDKulAu3`I+0p$@si!9mc~id|yid>mRpNZSDK7R_Z1UZqaEX2(xf`cmGQf}2X74iQ1s;D3J#A-xREX=HEp-=7_EF*8p-CW2-Z%keA0mC)bI=yru zr)FFTZT;~70Nd}x3uGA!xKPJ)VF3bj{S-NFvfhgJrQBeBwp28M|I4F)sGT;PLWmxM zvWa~D{0XQ%Wm}8tu$GKdR_dNm-i>l7Amr2EPl@5L(bw7)@Q}$Vd=qM|F?12`KP{xF zA`MSqQlLx`!TTro{bU;5lNmE*fH4CD75%@f_qlCI`(~yezdMLN>A4<3qj! zsb=WD)}5&A>FiD#K5m*e2DO6D`ldhkgy)Ufr{s;wmWl7A;>2aIrf9oiVqt=1bl9y6 z<@GIF>%IPN(hklpa znWe7#R~SD{Q}pB)Ui|Pc^9oEY0gY3E$6&Rp_PBySjucrG(BBFdisd?qkt+`Bv!*i* zX3>%nKstWo9bH>w7EkZ<6ZrQJZS|YcwjC9g-kT_Hh%5$L+l)Xeg`TR5(tWabdRh+ORv+f-OR!f_PC0__ z2~~SNZ6}&Az?1oMC6n5)di`qRnf?C&k*S=SwzXGdo1*|QSn&KhpNdrc&?ndU?=m!6 ze9{Z5Gq}v8nq_xUim0-~!byqu2D8O?ZdMf>{Vm6mi>KYdtpD@+mWcu zJ%_LPX?bJgb~or(iiXE&a$RE)D`&}>J4(gf=ERh1%kr)H00vOL5M!4Nk=|fuzxEVq z3i}@*ppe>mtVe~MOvNGI>bKom!_zI+h@cr)E5<=!k-fdXXIdUF*R*3;^JTMDCwVWBuM+p1v79_4{{_Pjr@yY^7Svvm0!c0tzB6Nu?I@ zUW{&!hFF32ZMA8_AyCt1Gnuu;C{&RzI*~b~u%foLCh>K4-qfL3BLt5CgBd#5W9gv) zDZ*Uj*qEV}wyCL6u{<~0%8fx_;Hdg4Us%KrO}P?RbBJJ>4i`m$G8~3>aa1T=%gSL0 z5nRjoQueq`I26XJ$_MC8y$MImu;L?+N(1E@uIPp*KR!{)4>YAvSD(qbtA_fACn*}S z6k3PSu;I!83HIEoZJC$v?P{+)^ALma2@k%t770aM7U^Xpc8(x{_0 zUd$ek*nm3vAzP7Vau9Smb6AL;Tq537p;OgAweQWcvSbktFiTlHiMz7{cR>0#nvgG{5x5)XRSeT-(2Do^sNk(D_$SoRn+ zx5f-?rUCFgvd`&YiP$4^HtaucPgHSQf85*%=N17`nKYogN;=%WS7(P~m6P*{+Nrd2 z-xs{tQt@xCfePB3ekw@g^ED|$AVIJX`hkuHf{nIgDIj>Z@-_oq0D>$dT$F=YtqZxn zkk0g6N)5~}0^$RNCCMcL~C?q8qEr{xzFmk&a-S@2&xxOVZW7C8GZLR=kXf~2Ydwb>^xwO>vPhkQKTmI~L3 zZ=ogHhk_5S{9CIg2CbPZ%%Y~M-VU|wC zG-Fm@Sx8AHQz4@W%9<}~*ibYC-S0&iET?j?os9fX^`QPO`(EAoYt^^d2D)hYj8?Cr z*o5iD@-|WF&u<|HycenvAz0rJldf_QiTC$t-Ughjxi#zHzfPMx#W3igin=SB5q@1B zD6uR^(|^wz4Xc~#^SY*A!S;+nxi9Xf(W_i*ekQ(DWPsu%%gKDj0t*XQ`r z%;djD^jY#)fqb$!GCqq$kaY8g@Fc1*a-2>ZX~DXWuK@4@x!vmXHEmWM!Sygr0>?74 zre-Rx;FtNg!fRj52FmBqQH+mMlNn71sz`nrNw!)$PIhp~Q&iB-O*D)0{&&IG?#y;f ziK|#r>>_B6*z_NFxV#7Ig?br!T$!fEj&CreH)oKn9-)YjlRFgFYZ;}UG>nA zQ^R!DvX3qC{*bm+a(3`*Zq4=IO|#D0SVU>%rYy8p(EdbADkU>jIhpxAapDVIN#M!` z5G&PJ-gZKl)gV$3L{isMnesis0|RE~D=xwuZF*J4ceMj$ZS@;rCZ!oC#PWmorrCYc zQPsq(1%iDghD~|L0d`&SsZa!$m)k_~uak(SMBj`vX@(jn_!qa;B&x1n#Ht^qQp~IN z_L|Y%1lqmK-*k?53EH;s9rN_!goruJgCw`zo~J?{4hO42PEb8MP470g;n1UMC(a?E zQ~<*`N>1#{!JngwIBl@0pg$Q^Y)g+QQ4PZ82L~Hk_~aNTN^~CS+R|1rcJ&!B$NHIt zwmeHlvdyN~t5g+*PGq@KeKOY%%V@I4UM5baW(|0GwR+?1G9-+^4#-*Esk^)co;79Z zI9gDE=lP4A-J8(1{)bS4&eEH!WfrY5)WG~je|BuO|BvX&jvkZ5he2R*R~Ow*!xTZ> zUUFVsDlApq@OR(qLcxrZ<%)4EHJ_~o`q)w z9GcloL-pP&!An?-DBIGhq6R}4#fqMg>VD5 z2!j+C=Vd!F)akh($02!|@r!SZoj2X{le*4wf?oB(NZVyP)GWUV6s%wTxTN$-Gxy`c zSNs)GPa59RMt47JWVm3dQQ8en@S|)%CuX2U3;i6mYh|9G5E8^7shAl7)B>mqHFGz3 zv(#}+Qpxo4{pnTt1gs}4DeuFWG zwqV=xv{!tKE!@bwxcn;Wah=b3a#~3E0W?;Rjvo{*AAi~+uxXn<8uo@>5B-^KU~zo* z44@46NfK&e3ZjMwCC8v}(vl%QUZTh%tJFhDfeHuo>GBEmOW5V{zmq`SjBTBx*SiUi z7k;Tg8MmutwBF7Z6itA_OSOhP><8wQ$c<0hr((IVMs;T>Jc_!;m9Cu8qra&XbX#Lt zPu1qM^E3g?nx-|FP7#|jXFp~upmr5s)xipP%aW#g|4kAVXeU06g78>9xzPAgZM~{i zs%n{&boRC10|44d>w5u2?HUra^hUtqOTG$vbnZm;kTyfzDGq+WO!St;X93YBjp_f& z9#(v2ieOU(7JhCtIXAyLKt<{^txor|IQ8fNZaF`+ly||O_#lqUdz}1RsGgTsLl6uz zL_0@tct+uml72S$w*}P?F3&}utF3)#X7;h|xxZ9L6nM5A*z_+w^8R1Ij?}fv_V06)t2&Azr1brFlOd_ZBXvdo4tl=L5%nE z{rQ52!%uRHFy6`!z(M*A*r9x&bd*=wdk23R-3WE>?b+|_Lw~=%3b)?MBBC2KtZdG=*!j;T*Pj`oSjlyw_|_0b`3 z5|{*8OCRN{=fA#Z8oN4lpY>ClwcbsIut?TNg8C&@rjh#JW|aThgReE|dcm^*l2o!8B>%v%^1gAS zw+QN*+H0&R^$}jrl)IN7PosTQ`G!m6Wf;2KyXE=TLiR1ES~XTws`M#}+a?UtFU*WZ zhAvdS=|U`vTO?TaUW+#xnOn-9WD&9}V>+)3bEe80t5%m-0W!f+m8?a|r_}TxmvP5K zgWFR&l5x8NBz!3R_4|YM|H$2u!G9gH_6uO(BG;FaW&UuO)@Ld+xk+y;;{8e8Ge>V4 z*}rE(mY)C7zb)Icjm$tgM-u)4!tKi&+kC%RhQ``p@O>J)9tJ{rNLqB`K_Q&aNJwHD z6wAv)%;=$wpBpy!w7f#Y?Ub%;Z%RFmtD1CRQ{4W}lt=+g;wFh@Tf_ zf=H;iEOhbyjb=?kaxAdkwBU-pm#kELqE_Fx`-7I*(RQ>-FbO#-%aU;mUe{+^Ft%fR zttUlnx^(5F*9|`2I?ps#{g6y^`iQYgZ_`;31+VncX;Cu{mUWz4rms<@_w4vfI`M3w ztpUludNlQTHMhtjygdL2j}!@aY>o&GV_=`*{WdwCHQ5g?NC!9|G)guS{il}1aTER8K47l4pegttyZ3>w+7f?b#5*nj-F38T?=;Kp3WULaR~MI<{dbIZ12f_?*_HVF#DJBwgDPRr6I z`nmn5QFOO(lB05UJ!W@AwN}QJee&pe@#jm0-&sRQEuxdpbBCP;a1VgRh9V{Q=#>deu#=hA)U1=(9*lWnsGnK7NALg}WBVq-=$2h%|t z06ko}56w!!anY}1KKgx*;V`)}3pW)N+@`U>pMeo2&VsV+wbFD#s|-Vjd{7AOeU?Cc z|Ic_GAa4!*Xr;PPfB3#XpJcKcRqeTHpV0Jg^RU#DZ-`r^2WK%j`bmby8pal}NaX{n8sQ6*i>7Yd%Sm;GEBk7YwrrUq3r=&Md!uXR zA(D4}&O6uj$Si8cK{xE$@yK zppD`K{U<7j3yz-q{#!Ovz#yN3khattOGM8|hG8WJOfz5Ekicmy)iRk1wwCq^-Lk$+ zl7A!Sa{1!|P%*-SYoWhJZ6dSe4rh&tsp^>D3Yu|Z7*9smN<0s_vq0ig2xfH=@qdfS zr?T>$b^&gYWiWHGsl(rNi(i6;bc_N+;ZeX;FkqGA^eu_J9VbrfEB^*bjK?h4aba0*$ zu-x2>&o#!Xoi7?d;S1GFqV>LB3*B-4S-)&qAF55%f^@`Nne_jXbvJE2cwG@*8D0X9 zh29;zzg^FGwP|HS=pRX$^q*$Ns+5YZ zyn0@9t#h24pi-qgn@ql*r);2^^Jz4GVVL*hmksFMM-$pA$XD~EX-05@XnfIQYy|J$ z*M4-r5~;lIqDVoTgN=kkMDVp!FRF>Fg|lAJW^^T*`LU%CD}bSO>!!Z9=57~=VCk0n z+WL1C05{klMwv7jgO7)7%*V;Bt}SwbZ>NxR_S^H2?a?sNt8eXB&2F$(f{t=i7u6_hKvKsiNVjnJ%fez(MAev#EEt`ef48=}9c~ z(PkQXa`qoG3pk{rkqzeMBm8NAh`VyfGIhdnTfdh5_wlxQSqaG%m#|>pkM_CDnpOuh zaK2{*{1CB>P1dMDTl`U*WYCD&v~ivQWC$~QT9>w(cMVc(awx}b1%k^g{vP`Pme;YS z^R}zt;o>i*+Z#pa)i0E}h=KYHQ5_(aicTa>J^~XgxS7>|_q|*HVG?JqmK~mRkCPLi**S}vV}!hrM6xusU7q`O`EsWmR|_dEAKu%5LwS~cb5M9i4PJC-HN{b=qzNzm9l}jP+eHw^&eNR zpZU(iECVUoF6CF++*!&R8DlboVK4A!3Uw=*msMw2+pMRT<%IOlkBz4Jv4vAl*SiTD zRjJ?2<4A-z>N90R`NIR&PzkXEm527Tr?&tNU1-VsSz9?g(q+pXVBO_iwG&(yNiS!eD*2Qt<{x60rN#FropreEkc@ zMJy){Y)PL2F+^Z#r3rVlqv51!&f|Wi5n*|K?m)P{7~;7yY5a0({VMlQ!4ddS{ct?7 z#9XvlX+tNY_&ZtR{RTcb`9b%l|D>XDhkmP8W*+(0Mj`A&;BsH)NcCrtmc4O!UXqNR zrF@!xPrfnBsyxMt@aWi7&*tWKVN&2gnsZo1Kdf9}tv6P<|9MPA}#Qv#WuF^le7XT?HYz~&aqTUwwdUx_s;9%b3TEl9Pt z`Ese&qBdZ3{qWnmyjIgsqgER=U9rqgy}+`o%0jYaChnbnij3}>e+vXa{5KK~*~gTN z343qr*@_T4tl#Iefl@Z|o32?6QtKFo2z}@2Y@*b#KhB{VL9GB-Hl$4r4B`Fy{m3Fe zOZ1v6{r!3ScU!!`(7E`xdmv^>R-h==jcPT(UnR(p=($d%mZ|8jd?_lxmTA z=-6-mWr@~#&9Fa*c|VE85Xh((8VbG-_zxgt`%Eehd?6PAa3jK2@4yY*hT-W z6XPm!PF8A+WWXv})mcouG6vVlz`U3MwrV{yspA~AZaj}AD>Y53xJr7uVe#~M%|rk( zmd;foOEjsX&YD{=^G?&!e4xxKz8?Emd!VaV3jg}}&%HWe5GcVS`N>j7R2F|~4ye0j zo0um}Y$3-WH!=M6p2UQ&HtDStXU zLA8p@qWlDYGA`F5y>Sa>h)^8qBgq!!+}7d|%sj7V=YOO&cE6A1v)p(7z{cLVcjxPe zf%mV~5>=UPeRps63sNUueVKG8nt1YTq$)Xh+FzP!YJUpt@C*(sinIi}@sE&H=fpv} z)I_QED5=4Jp9n4gqyn9lX{RV(+u~>grf>Tg8WmsM$t|*OX%jfZ%mG#Pr3;a>9SSPX zin=YIZODr$A7}|=O=+J4(GY>t5d`SFaqxgn#8uryFGdlt$gjCUFG3=C|Cpu)u3|lx z{N?3Sm>Z}l#A28wdFEq|<z^>f#aqQoR#-9p26Sb(p@xK=>9R}J@uqpU- zH`AgypjjXV2??)Z`U4w5R~Hj|A9H=fGNFQO)wD#2oD;cRzz(U^aFeDou#~g4W-7eO zi)L`Xo7TwtL)54o!QG-$o)z5K60#t7TuVt_0BSEMLPoxu8y@35txA2vN=QDqmujKh z3m*h(=fuw!(C7OPYiFCiwUuopPzI|kB_}geo-AkN(O!O)68W$Z3;u}HB~bfp%K#+! z>$cIOKT3^&pB($dn%eDWoeo)% zWzNt@3$@2sQh9<~M1%u(saZ%!4O`G8>Vp;1;r??X>h0fQ%B~R5ck9V~;YgggfV~^F zmX&Utg7V)djNPOz`X>?Tdwv-JR!yU~`eD#p>L%)5smv;sUF8|mNJ|bju!<&_Qfr?8 z*kW+-JJ3OA7e$MPPOIvUQ0|wKRWhrN5D5)r_I$8CyxS47O}NI)p+{AW#a$`t6S4-P zx_LtlfV3Kv#J^<-@48yrCD&PUN8ug_38SU3CD387uLI?ie>*J@}^*L`BFEoTXk(I)hi4+Zhb z81MSEb6mi$=-W0^gqLbZIf&ebENndhRT;b}vErhmG)Q#lAen57fBN zDkv49L&$13iX%heAk_m;RGwSmvV*gJ(%VIxq3}iB*ZOqwWo#8aj!*h&h!Mp1C1NRx zXi(U&CSOU|UR>RIL$i792^B?yBhfTpk6< z;ed}nQp1|Toqqj9D!p8|3I8VXpv1X`z98hp+I%dFC1Dsl^m)(EN=lo(xk)TzIkOI? z-&k~dn3Q#h5+-IH?{sJJ_EpZ;Pal&dnuq6jgWf9tiRG9o&GVFwau0YkQ=LKgxqprfpaNat&t^p4qN|O6c`1YDtSyj`Ku1XfH-~qwLp>;7 z8-l#~T78dz)abIg3Z1ApL6lPO&==LL5+auKHQqw){FE+@=j&iSM}YlAo6I)OB)M~G zu5S15VS08!#Sbf5GKzh_o4Nid5lUrcVD1|@QaCL>B0Z(u1w>Lu`vH=W z0Kqb~Y4H(xO=#TT#lG6n+57P%J{$OI{91C_+3In~> zy%G+zTMV*(b;%^{3G!CG;hX(|V5hh$icwvZO8KOT8ttizN(tWx!eu`%v>@hc{xuPD zFqREe0cbR;Lp~wCeciDl71$D)Y5GXHl?*xMeqW)#zm6z&R`IpEFl|6@x2% zU&-ftJ49zctcDBKb6!S8f{~{7*lF3hu^bFa#x zULvo2iJ$%-nl97;P-^Yu8O`+1-<0I~+x9pA15gS#^I0vnDR7f(+^g-Co!Gn@o3>|` zdf>hpY3WTSrK~1>{1iN2j$Hdy>M{-Oq~hSgFSOovZflGrS9KQ@1DZl|88_AJC&RKk^$FNkbv#!=R%wX-Hy-feF_<3vq>r?8(_Cp{c zfOW9t;=uQ=g47?~>zl=6e3l!^bVpDqkww)%j*>glqHKUSRC}oZN}%|R0N3Bxyjh?% z*5upFG%P03BXqr%7x#`Z$oo+1mLUa?{KwBd4-Th1hS|-023*3dJ?)8ABR#z#$k?)G zO?pqPhPqGG4-c5YE{XlG{xf|k%9uZ>p-u@ViS*@*Vf}cDH?~goXBYK@2PS(~9BSy= z1V+s;BhlGvZ!fz!`F58~CxU!vbD9zO^;8paW!5l#=iSfLp|eSO{mduI*Zl$&`JW{f zmDNiYMKJB?6cSKsvFbHUNILIAo>t{36Q(o@;KX{m#ysvnCoji*WYTwa3xR_cXi{q^ z^$u1c&b0xAGN|oy}SYx zs<#(>kbXg03%RJ%ZlcmzYB1tZL5|ApLUx3K&(}PvVva^c$JJ%gff^3+(^jX6(|3#X zQPv~~_Xeq`KYa__R8d7D8j1SzM*zOvd6+Tb#GFkymNBO%bIS^mmWd6Nw&?SxbuRi3 zkZuH2{pDWb>qvw^q=K)J+j-j(7-Z~s)93p+1U?eu-n_s8`k**g1(m6WDuef;v+c3?+U5`8tL| zvjaQCNy+tR^unkP)^9r`{7A$f&9J8ijv46f{oJXqV=fkVtHhw8caX zy*Kv6gJ6D6AkEPt!M{ggk@F+d@1v3#%H$Ax>Y9Pk^{k}Q^rI!$c+%E!K`68GA(<{_ zUJ7z;(1DQN2jF=JUsbwvTg=uCbH)M^Yh18b;lncFP!J^wb@IcuBrugdIDT=v)(jqA z=+tJOMen5lAzbM}AbiLoKnG@XRPtrJZdB#E&|K!3VasKmom&|AP{sxNn=pZiJoPom zf<5&yV!S6|A%LVD*)j9JGcZ7{lI_hXR#=cTnCTD!RR}W2Q>gY3o5{0s`YkCMX`R~? z`MlH@FR&AgJ|+!$T;x>8PwV2u?d}gGpH~I)S?lsDTG{yTsKjjXEx09*U0aU}zqK1m zKkYrV_-@tBctkmxNV*U=ipe5!a&|{(n2|i6iayJzBOq4RWWxb#@Q=9{^0s%z#_I;o zop9uaFykX?dY{#?+j=Ru1V$#p-71f~Uy*zLJLThy{s0(bBz5CiuNT&)qARFp@Yk_V zPW2`ru$?wBv}_l1GcViJ&uOaOVc%#ZO(&=rKxUVrD)dPUeXmQ7gnFDBDkA>1*g-cG zDx|^bCnh{aN&RlH$b%Z-N%*1Jq*Teqmk2rb!Q3Lah)1)^6kRcPx71fl8GuGxIQwA| z143JbZ$H7621o!$ILzc910b$7zIX_`ObvK5PK9O?WmpTXRRHRf$g>zK02+vmW5^=O zd*-C4pf=yyn;Z+)m9=J~pOE+(MRtgy#IlAn!ttQ>O5z3u6g71HPb!En7|8+t++%HN zn`2Dn#^Q{Ly}&;;EGkU7WCaKJ21nqm@;RV$q}Y1$J>tc6PmhB%)}q@v9`v0k&dV?q z&4JQtIjA)_b!~+MofxW2=-xaTg`AjawKuO19BHz4PR*0V)%!l(B0*|I6rYL#k#d=V zRMhcjcv8Pq<%oZJ%%#!oU-0*rJy~fcsRTpVAo~T(aTqGUkd?|HbX3QFg`x&d=0PCq8Ry|}3lcWe~q){^8UsUNU* zwZufe;@^F{xOaI%f2m85-%;ydgGilIoyD^Zdb5L$%s`LzveoLFyO6#m(+P=d~V_Ebp{NT?}FLeYCMRb9_EV zKYMD6{w%}(g=@&gg(QO0!kGyg(!)~Xk?1j9U?F5F1LIK3sitMzBsHf7SJa1ruKFmGVvN936J6~K3&X=2CjecHN z)17b^77vO&F@d)Po0=$1#K!!D&C8OT^^^{x`Munh|AMD6YdNXSErx5w^=io4bs)3D z{f)s|oh=MW{CJJwFM+L=EaIE6=;uWL@0oSAjif#m58gF@P?B3;Hqo+-IJbJ4!k*I>3uw2H7o{~w^Ax(krr>V{!DF@Cz|lr2S3 zR;ek95XvDY)c0SlIEpyU@^Ns(MT>X+%ba=k^CZSKBIWu!@1%lH2ag|2n7$-y+#i8brk+VSQq@>yGd7Z?oe2YG`(uoPqKQ*+T zu+&gFEetn}7X9JE_JA z2cB=1$+Z*D!J~08ihs(khH0@P*RyhJkAVt#$Z;FNHI%(j*1NG6kG34BBE4uG?Tt8_ z0zRlaCyPbk*lx<1s@h+Xya-h@^L@{(i@yjH{-u0$mHt`hDV0Q{+;Q) zv7km7uIF3G+2-xMeWs$G(VbM+J9ZTqXv_GKS>d`HIc)UZKa%@@l)1pWj|D1`0bUx9 zvBy4+wd{~yX_@zO;Vlh3;D5tww>rHv`kvJ~WUYusf2{fU{Ao>D{83`oYcj-$^ssrp zWepQrW4#gSA2s#JZwEu2WaRC7JBmHfQ)(xR=dc0fkzP*oC*S?Gd&~T+N;{*6-a5xB zEkl#T;u{%KD~We7{K$~HC}RG^D!X)62=~ER{HajZXp>4{OfK*lq4KzPC^bHNbOq;0 zFAX*9J_yoFQi(X;C9+nu1lyd+;Suz?X|bQ~XVz|KO@RNMDz(Zn>^Dq3(0KYyy#^N` zD5(+k<;N+*uWHqIzW??cbTI7(?N&%C!H8L)6DeV)hRFl_Zr-fQY0~3NXSIF`xx^`p zt>hI0EuDLQq{61ZEBfVk*LvttZ>RNj?xCkr4MI%MuxC9e^Uwj2ef&!AHK*IC@q0)5x!TFs}a`{M`Xbi46ur-Bc|*3YZGhglL6@GxAtWC)Axt zzJ(8)C~<|z_ph&sen+i1J_(d@iYW1Y1;Tt6XzdKvlV^n5bP= z^rqYYtTlj$C{@l;Zg5~}r2M?VsjLg?K6DFYwQq5uiT2eSDz$3xiX52UkoR!JsaAE) z8qHimrK?c3MlHwRJlDuHHWc+xX$WS?-LO^P9V3%>&@*j{JwaLU=mZDfv-GAB4YvN% z!e=#iER;1a7BB!;sjVIIZKdpFrckz;e?9VRwZ3&U?gQB}fxPOQOkO(%Ho`F7zx_%X z`<3Zb>6_@JxF~yZQkYIx{4f%Hq9iGJ`fF}JdVnrhIzbFzs$Ab}+~PDsoilA(2b&4* z=Y5dpUzw=QY=hL=yepJ7Mqe(5uo0N(85m`C*he}he>nSrd)p&0Ge(`io3l}t|3jiB zX<-38qP-GSBq9qmy@%`a=WZ+;%FI>);i_AlCRSbb`J+wAe1wVyK&Eq}C11wDFOSKL z(v~x9XcrZZujo2ag7dGCbbXJ|M$vk9U6~Zpt#^MQ+hgfFI!WCqD0(1uC0{3$*C?-Q zT)+X7(%hOgs_56E#DcUQ-G8`yRFY*BZw-KzVG>f7z&bTO=v_Eiy5@Z`u|>jd@v@AD zX%cKvwEGu4+5-bdgw7h)TFez(d2JM73+YFl$u!3k7c$h%sgA6MgU|yhepl15Pz!-L zq}w+#F@4#*OvpvW4v%2oAexHq>wH!A+Ax)k*l%msedqM_N}k-I!qPjZhzmTZoQs{h zTPS5Q&!YSIyw3Hyw6vF4u{r$m@pEeU6AoG?Suy5yC1UXm`2I=B`(mRzn>MS^aPE3h z7_oOP2@oN{dBx#@B8_n#F@w~H>4rP&n-zWqzN(-kpDT-9ED1q*VOKV36P%umi0Cn# z7LC|>0m2&cMWKN&ut~^^`e5x>B=+_x7XhLl>qi4@@2oC{q?)-@>j4 zFAw5OFMA?)cti3+E`;5$xa{#Xf_xb*j^xSX&tZ;R_YFfm9&9qPSxs&rjeHpzg#BUD zxamsoi1tND6uYbq*p?(LCS$;2|H7e`!bnE3~#ooe4Pruro5W7mk58o8Ed zwB8K!IIUn2H=lP0F9bUbnGuuBtbx+g`dzK$f>Zq?05CQW)^-fLyubr9`HIgFQ|ozp zrhgp`y7j|1-4_JAAYb{dXNl7JlB^I{Wb6VE-C|xsFCNm z3Nc6as)s0e#WD7b7(_TbIJ$p%5s3dQRN?Jrb3cK2gasQ&_79uQ*QCzqXN?`!R}$BD zki0I+{;}JUJP$oC;IXfh9t|T^DA7{dVyW6an6owqiZjVlI66kwfG1k8{XE6K%6%iQ zMuerfme%wcjrC~|EM@NQMCF1!eEP0)IB{^UO5D!4d*NJaDROH?Dtt_6-p)!m-E%o} zC;4Hi+dQi9okoMZyMBMG$bSuRF)d=3a*dbJWHUJoly(P%pzwXP!7^XY!JkiKw)NhQ zADB29g|BNzVefW;S`ee+l>~mq5JT)xs$%xj2CJ1;XYvU`tx}bdH@E>YJR5|d1dW(Y zbpL@r6`aHfc5T_ZXRlZ4#!Ztv+@_f&AiB&b*322+0<`3;;N+*B@dwi8xjB6KHBR?U zf<(zO*x%X;=#v(YWU;h7eB-{eyoiJawdRqv9N}D^Sd%8wEtFIzb)$5!vmH_}Tv%=_ zC&_P1ID(vbz5Yn8ogbeRzMYsAEUBksH1|)_tF(lL!-WIUgDXR{RFnF zq{ju&N2!1bmRXi_8QJ$o}7AlwP}sj&OpWFjw=qCcl^qLP?G`vss7%uwT?dRQc~ zU-_(nuHHL+5Q>)lfuM@-9t%*7H!E29Xr1kd)ui(=Rr+B*zAp_BV^fc7 zyf(bIUECMI-J;2{4=QrrjhLql;aLAHAD}<3dK$T?+uEclgcgHE`SW2vljEPbb}vhx z7^;p$)&Dv%?FQ72f1ux7V6v{&8Ie7209wSp;E*p3-j-Na7M`!;$|rv9+Jd61Y@7sX z7X&cy>>(C=^ZQZwE-fqZV}hi1O-&;kQ`nT|GCs0Niah$D86FX1J(+k-2@fxGZ+u)< zJpFkd7qEzCQH|*^7k`D;FIM@K_9UHMmOcpO;lglMys%3YZ{WqBU@pjcFqjCAb7cO3poR$4`$qHK3jc3AA*&ndHra&`}##A>tbk(mv> zLKe9%hOJHdPR1}dMWd50rA&-xHED@mFw-2q^R4*J1XT$OZI@!a3r>G(7`O1HWDRTjdHFHHZyFX-fY_33M7B3-oD+> zTbxpBv737|B5mv1p{--A8L&1tB!54xHuH5AJN;JkAGf*&9vcYrCjD}?(H^l-^a4*7 zT4=#oU;MWWWncM*$TdYpoA@-;pL47Ea$WhjnP-3(Ok&9zyA%H2=xH>Xh&PuM4to`F z{oa>Xl>+n^Pf_?r>vB2x1STX_1-5tv5y1gY%yI9+{nc-`deSd8rPYH19_*S8D(5FlS6fs-9`z zcbWoVcunCwx=u-Vh5x6EFJ_zUxt~BDIZPG?d`foKx)8$s%txIGDc|guim*Ezh=+_{ z*1p@&iAn{iw~x6dhI_i5XvXn@5j8S@Y2M;qS@8=vIZYnICU~L{8tne3?y?3+T zxIyy9qJ5Hcb+WW79&%bf>k&Li^l!~^lHLyS1S2E%pF47o>Y-`R#CB;AD1 z^3rb*u|QQqYJ>P80RmSOv5f?@e*ZP{Y{@->#jpZ$Q+-laSN2Fk>oJTcsRFlKM9E(2 zWD$7``s?MgXV4z`vExe%EY_eyi}SwO7c>!*y|7l_@?!r47eVRzM(FtW|0d%lukkT? z)ljCLtL%)PhBy&QE|z@?z)mB(rYB?>12Ee%;_gFlW|dt`J8G$nng6LIR$~;nWu*iM zE$$aE9oqfsmH3g0UXxK~5_nw$o$pwL|7mfV<)}?x5njtA=3(#xI)pLuO`@tSv)wNc z4mRhIYk}V z)DqOw*~_bAf<6lp1+W30JvMJ1>}N9%`7CeOm+%NNHfx-UY5$jdFw>aG68G%FY!0E= zyM-z#yKirsBdO~p`2hgq^snzkKTZZ-GwKJO=p{IGhaxmH*w&XnnR~eu${va;5`P`x zw9{2(#yPf363p%xzr5y_V%efxz9RAyy9Do$Kh#ZB*CWgq zHtb;JL07~~(P)xr-S13h`zAr)UThKJXHA3XxA!Uar84L;0xO^U$V27xI`PLZ!_d$e z=z{97)6rz)yxd&f1mS;`N4kBrM7L8{AP>_uw$M8CvToZ0vy1GzAsd#RaLIf@9gV=C(bJYNY<QzYQ#E}Ygz&F_oKpIYB-VrJF+^#{o_K%GWsZ~1B3lhl;ud6^hjg06ci<3R6w`G<70 zCI1MUmf57F@-x}*alNSi^urvM?L3l9IU0j3_a^N$_j=sYtt(s({nREN)F#Gk%3wH? zaDB+E{XrF7!=Yg~H~;aDe~O=bOwr<)cY+D*R z+e9UEY>r4|tUamtdgZG<3Vr%lSap8g zY%}Ft&qJ$N-51RF{sYwMjuLD9<(ETQiv^<+?tl3G68Dec{F|29Ela$i#5mmONo_N| z6fjz(d(_`Y2;ic{?W;vjL2n&V0ta$@q1KqVca~Zlmj@vh^3T6TcYrjrcBVGgXC2%O z6Tq7Kksa2S7Oe~f8$ru6VoX=uxhzg|cYcsPe8Y&yF8Mf=o**pz)`mg21jS@cU!A`r zDHhZzzw%7h3Stg)%JjqGEzoy%mCvEp+wQu2qOkMrDomPhFqPmpQNLW-ZIR;S3ng&v z=Dk7^A}wq&~WDBbVW_EX^(dn3dKW=+SC6XDdy!E~#+jAp=U2lTi2itKVLji&@w+7HU& zNLCfi1(M#+IacF7r$3RvwDMe%kho+pH6mWlajn^E*(NjVHf}2^y3SqQDUw<;+RWi@ z5SW!(#Ervc8EYkyZ&u2$d1NLVK#fPIeFpCk&0@L%O4AC<^tPu3UioS_i%I$(n=$g7 zf_39&3-X>Skx5PoN^(u4K|`xyhEKU`^W{ zOMhJA%Fegzj2SUWgylJz(05_HaIYT*F9l=!Dv0-hErUPQUBx4_d|JCpI=~GqJ|RWo z>ncwZ_Y#%H^u@rzPvJ_2|L$$e8mC8_6Eahza~826^jhy9_5KBY{m6+A!arFRIZB#I zgE`JlEK>w^udPBJlP)E}z266A{|E5;GTfZv9(vMEVk7H~7FXD9-pn=%u@KeE6t|V<=yj$a zg5b%~xvw1E5|w7B+dEZHq zeZ3(EvFz-P8l4;(r^NBs&)u`+SJdWDz>i`0Ow{|lK!yM}3zl|L~-UPkF~$9<9~rag}G116w`N&uxw zSjr=qlJ9ZwrUL{)#r5=}tRyh!g@lPf8KinH)N5}{JO~GHBY*U!h+Y;^U@<=0Xgq)o zt>ZXB39k(So}QQv_NXvZr}qNrI#AUzZCa%YnzTLBO6OZVms?_YMm>+4Jv$lEw zb%{(PFxNQ=;>je28ojnYy-%Wb)IA}Ne-hu;3gaEhxCs;Hb6&yeJ$tpwu&Av+% zt=G?(L&kX8efiJUT1(U)Iiww#?z+dYiVp+P6U2^_5tQvZ=PrXUBOA+1FpaZ>5lqLK zpgvM2)3Y*4bA&U9+RVRMU=ldA_@YOCEZ_gYu?AQ-OpG!Rp|CztRU*_uXiWvSc@(OAEA8&5UWX+nKMhD{eLgG~7W2 zogNFP`jH#kprG89CB82UEV=*a%l-x*5mKYsktJPkz@-)c+E9kH^$EEbv48 zwWVs{$hB6U9L#u18$i(k;C~UEC5uaAW`i=>3Uo-j6bIKd%4`eId>a5Q$OVv+L5gxx zpUkiNYeV8OQdhOR^y5>h@77K9_D130U8!%#8sdQ5xC5;VR#}+;4Zda+ssHM={3Ry7 z?4ovED?sV8I-Akyz9K9r)37PvPS($-mkm6+UMIUOCl=EJqeH%6`U@j-$Q zmn@>KI-+yFyTD_)c^s0;L=#*I+Gud0&vri}NmVU!6DkN0!W`3Odf=T|ivBE4XB(

c-^-dcG*C^J3Dtrvd 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 X4O Date: Thu, 27 Oct 2016 14:19:43 +0200 Subject: [PATCH 17/24] Delete dual-channel-gw-ttn.jpg --- dual-channel-gw-ttn.jpg | Bin 132430 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 dual-channel-gw-ttn.jpg diff --git a/dual-channel-gw-ttn.jpg b/dual-channel-gw-ttn.jpg deleted file mode 100644 index a67ac7ef3a3669f845e6eba3f349521ad27959b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132430 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%0Ddh-!cLnk( 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

c-^-dcG*C^J3Dtrvd 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 X4O Date: Thu, 27 Oct 2016 14:23:29 +0200 Subject: [PATCH 18/24] Create images --- images | 1 + 1 file changed, 1 insertion(+) create mode 100644 images diff --git a/images b/images new file mode 100644 index 0000000..9c595a6 --- /dev/null +++ b/images @@ -0,0 +1 @@ +temp From 8b7f95315492b80eba25fa2fb5173c9aacb8aaaa Mon Sep 17 00:00:00 2001 From: bokse001 Date: Thu, 27 Oct 2016 14:23:54 +0200 Subject: [PATCH 19/24] Delete images --- images | 1 - 1 file changed, 1 deletion(-) delete mode 100644 images diff --git a/images b/images deleted file mode 100644 index 9c595a6..0000000 --- a/images +++ /dev/null @@ -1 +0,0 @@ -temp From e8f22b0c0a681548ab241cea7b85e3c32e7c11f5 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Thu, 27 Oct 2016 14:24:19 +0200 Subject: [PATCH 20/24] Create temp --- images/temp | 1 + 1 file changed, 1 insertion(+) create mode 100644 images/temp diff --git a/images/temp b/images/temp new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/images/temp @@ -0,0 +1 @@ +test From 54a993785c03d29bf8931cce886df5fd4e085593 Mon Sep 17 00:00:00 2001 From: bokse001 Date: Thu, 27 Oct 2016 14:24:31 +0200 Subject: [PATCH 21/24] Add files via upload --- images/dual-channel-gw-ttn.jpg | Bin 0 -> 132430 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/dual-channel-gw-ttn.jpg diff --git a/images/dual-channel-gw-ttn.jpg b/images/dual-channel-gw-ttn.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a67ac7ef3a3669f845e6eba3f349521ad27959b0 GIT binary patch literal 132430 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%0Ddh-!cLnk( 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

c-^-dcG*C^J3Dtrvd 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 X4O Date: Thu, 27 Oct 2016 14:24:42 +0200 Subject: [PATCH 22/24] Delete temp --- images/temp | 1 - 1 file changed, 1 deletion(-) delete mode 100644 images/temp diff --git a/images/temp b/images/temp deleted file mode 100644 index 9daeafb..0000000 --- a/images/temp +++ /dev/null @@ -1 +0,0 @@ -test From da3de98b67e1d4c351057b7995f74ea4db164fe7 Mon Sep 17 00:00:00 2001 From: hans Boksem Date: Thu, 27 Oct 2016 12:30:37 +0000 Subject: [PATCH 23/24] new file: include/rapidjson/allocators.h new file: include/rapidjson/document.h new file: include/rapidjson/encodedstream.h new file: include/rapidjson/encodings.h new file: include/rapidjson/error/en.h new file: include/rapidjson/error/error.h new file: include/rapidjson/filereadstream.h new file: include/rapidjson/filewritestream.h new file: include/rapidjson/internal/biginteger.h new file: include/rapidjson/internal/diyfp.h new file: include/rapidjson/internal/dtoa.h new file: include/rapidjson/internal/ieee754.h new file: include/rapidjson/internal/itoa.h new file: include/rapidjson/internal/meta.h new file: include/rapidjson/internal/pow10.h new file: include/rapidjson/internal/stack.h new file: include/rapidjson/internal/strfunc.h new file: include/rapidjson/internal/strtod.h new file: include/rapidjson/memorybuffer.h new file: include/rapidjson/memorystream.h new file: include/rapidjson/msinttypes/inttypes.h new file: include/rapidjson/msinttypes/stdint.h new file: include/rapidjson/prettywriter.h new file: include/rapidjson/rapidjson.h new file: include/rapidjson/reader.h new file: include/rapidjson/stringbuffer.h new file: include/rapidjson/writer.h --- include/rapidjson/allocators.h | 261 +++ include/rapidjson/document.h | 2014 +++++++++++++++++++++++ include/rapidjson/encodedstream.h | 261 +++ include/rapidjson/encodings.h | 625 +++++++ include/rapidjson/error/en.h | 65 + include/rapidjson/error/error.h | 146 ++ include/rapidjson/filereadstream.h | 88 + include/rapidjson/filewritestream.h | 91 + include/rapidjson/internal/biginteger.h | 280 ++++ include/rapidjson/internal/diyfp.h | 247 +++ include/rapidjson/internal/dtoa.h | 217 +++ include/rapidjson/internal/ieee754.h | 77 + include/rapidjson/internal/itoa.h | 304 ++++ include/rapidjson/internal/meta.h | 181 ++ include/rapidjson/internal/pow10.h | 55 + include/rapidjson/internal/stack.h | 179 ++ include/rapidjson/internal/strfunc.h | 39 + include/rapidjson/internal/strtod.h | 270 +++ include/rapidjson/memorybuffer.h | 70 + include/rapidjson/memorystream.h | 61 + include/rapidjson/msinttypes/inttypes.h | 316 ++++ include/rapidjson/msinttypes/stdint.h | 300 ++++ include/rapidjson/prettywriter.h | 207 +++ include/rapidjson/rapidjson.h | 654 ++++++++ include/rapidjson/reader.h | 1452 ++++++++++++++++ include/rapidjson/stringbuffer.h | 93 ++ include/rapidjson/writer.h | 395 +++++ 27 files changed, 8948 insertions(+) create mode 100644 include/rapidjson/allocators.h create mode 100644 include/rapidjson/document.h create mode 100644 include/rapidjson/encodedstream.h create mode 100644 include/rapidjson/encodings.h create mode 100644 include/rapidjson/error/en.h create mode 100644 include/rapidjson/error/error.h create mode 100644 include/rapidjson/filereadstream.h create mode 100644 include/rapidjson/filewritestream.h create mode 100755 include/rapidjson/internal/biginteger.h create mode 100644 include/rapidjson/internal/diyfp.h create mode 100644 include/rapidjson/internal/dtoa.h create mode 100644 include/rapidjson/internal/ieee754.h create mode 100644 include/rapidjson/internal/itoa.h create mode 100644 include/rapidjson/internal/meta.h create mode 100644 include/rapidjson/internal/pow10.h create mode 100644 include/rapidjson/internal/stack.h create mode 100644 include/rapidjson/internal/strfunc.h create mode 100644 include/rapidjson/internal/strtod.h create mode 100644 include/rapidjson/memorybuffer.h create mode 100644 include/rapidjson/memorystream.h create mode 100644 include/rapidjson/msinttypes/inttypes.h create mode 100644 include/rapidjson/msinttypes/stdint.h create mode 100644 include/rapidjson/prettywriter.h create mode 100644 include/rapidjson/rapidjson.h create mode 100644 include/rapidjson/reader.h create mode 100644 include/rapidjson/stringbuffer.h create mode 100644 include/rapidjson/writer.h diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h new file mode 100644 index 0000000..f615ffd --- /dev/null +++ b/include/rapidjson/allocators.h @@ -0,0 +1,261 @@ +// 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) + AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); + + void *buffer = reinterpret_cast(chunkHead_ + 1) + 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; + + // 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 == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + increment = RAPIDJSON_ALIGN(increment); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + void* newBuffer = Malloc(newSize); + RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + + //! 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. + */ + void AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity)); + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + } + + 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/include/rapidjson/document.h b/include/rapidjson/document.h new file mode 100644 index 0000000..ca80963 --- /dev/null +++ b/include/rapidjson/document.h @@ -0,0 +1,2014 @@ +// 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 // placement new + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#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 + +#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; + +//! 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_) {} + + //! @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 + /*! + 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. + */ + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer + /*! + 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. + */ + explicit GenericStringRef(const CharType* str) + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } + + //! Create constant string reference from pointer and length + /*! \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. + */ + GenericStringRef(const CharType* str, SizeType len) + : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } + + //! 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 copy-assignment + GenericStringRef operator=(const GenericStringRef&); + //! 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 + +/////////////////////////////////////////////////////////////////////////////// +// 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. + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { + rhs.flags_ = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +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_(), flags_() { + static const unsigned defaultFlags[7] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_ASSERT(type <= kNumberType); + 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 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { + data_.n.i64 = i; + if (i >= 0) + flags_ |= kUintFlag | kUint64Flag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { + data_.n.u64 = u; + if (!(u & 0x80000000)) + flags_ |= kIntFlag | kInt64Flag; + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { + data_.n.i64 = i64; + if (i64 >= 0) { + flags_ |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + flags_ |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { + data_.n.u64 = u64; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + flags_ |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + flags_ |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { 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_(), flags_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(flags_) { + case kArrayFlag: + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(data_.a.elements); + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(data_.o.members); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(data_.s.str)); + 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((void*)this != (void const*)&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; + } + + //! 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: // kTrueType, kFalseType, kNullType + 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(flags_ & kTypeMask); } + bool IsNull() const { return flags_ == kNullFlag; } + bool IsFalse() const { return flags_ == kFalseFlag; } + bool IsTrue() const { return flags_ == kTrueFlag; } + bool IsBool() const { return (flags_ & kBoolFlag) != 0; } + bool IsObject() const { return flags_ == kObjectFlag; } + bool IsArray() const { return flags_ == kArrayFlag; } + bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } + bool IsInt() const { return (flags_ & kIntFlag) != 0; } + bool IsUint() const { return (flags_ & kUintFlag) != 0; } + bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } + bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } + bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } + bool IsString() const { return (flags_ & kStringFlag) != 0; } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return 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 + static GenericValue NullValue; + return NullValue; + } + } + 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(data_.o.members); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + 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(StringRef(name)); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(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()); + + Object& o = data_.o; + if (o.size >= o.capacity) { + if (o.capacity == 0) { + o.capacity = kDefaultObjectCapacity; + o.members = reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member))); + } + else { + SizeType oldCapacity = o.capacity; + o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 + o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); + } + } + o.members[o.size].name.RawAssign(name); + o.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(data_.o.members != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(data_.o.members + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) { + // Move the last one to this place + *m = *last; + } + else { + // Only one left, just destroy + m->~Member(); + } + --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(data_.o.members != 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, (MemberEnd() - last) * sizeof(Member)); + data_.o.size -= (last - first); + return pos; + } + + //@} + + //!@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()); + for (SizeType i = 0; i < data_.a.size; ++i) + data_.a.elements[i].~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 data_.a.elements[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + 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) { + data_.a.elements = (GenericValue*)allocator.Realloc(data_.a.elements, 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); + data_.a.elements[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()); + data_.a.elements[--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(data_.a.elements != 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, (End() - last) * sizeof(GenericValue)); + data_.a.size -= (last - first); + return pos; + } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } + + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((flags_ & kInt64Flag) != 0) return (double)data_.n.i64; // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return (double)data_.n.u64; // uint64_t -> double (may lose precision) + } + + 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; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } + + //! 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 ((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 + + //@} + + //! 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 (!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 (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) + return false; + if (!m->value.Accept(handler)) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (!handler.StartArray()) + return false; + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + if (!v->Accept(handler)) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + 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 if (IsUint64()) return handler.Uint64(data_.n.u64); + else return handler.Double(data_.n.d); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x100, + kNumberFlag = 0x200, + kIntFlag = 0x400, + kUintFlag = 0x800, + kInt64Flag = 0x1000, + kUint64Flag = 0x2000, + kDoubleFlag = 0x4000, + kStringFlag = 0x100000, + kCopyFlag = 0x200000, + kInlineStrFlag = 0x400000, + + // 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 = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler + }; + + static const SizeType kDefaultArrayCapacity = 16; + static const SizeType kDefaultObjectCapacity = 16; + + struct String { + const Ch* str; + SizeType length; + unsigned hashcode; //!< reserved + }; // 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 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode + // inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(String) / 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] = (Ch)(MaxSize - len); } + inline SizeType GetLength() const { return (SizeType)(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 Object { + Member* members; + SizeType size; + SizeType capacity; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct Array { + GenericValue* elements; + SizeType size; + SizeType capacity; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + Object o; + Array a; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + flags_ = kArrayFlag; + if (count) { + data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue)); + std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); + } + else + data_.a.elements = NULL; + 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) { + flags_ = kObjectFlag; + if (count) { + data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member)); + std::memcpy(data_.o.members, members, count * sizeof(Member)); + } + else + data_.o.members = NULL; + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + flags_ = kConstStringFlag; + data_.s.str = 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 = NULL; + if(ShortString::Usable(s.length)) { + flags_ = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + flags_ = kCopyStringFlag; + data_.s.length = s.length; + str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); + data_.s.str = 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_; + flags_ = rhs.flags_; + rhs.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_; + unsigned flags_; +}; + +//! 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 + /*! \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::move(rhs)), + 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 + + //!@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) { + ValueType::SetNull(); // Remove existing root if exist + GenericReader reader(&stack_.GetAllocator()); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. + } + 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 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); + } + //!@} + + //!@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(); } + + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { 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 + + // 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 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, (SizeType)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.flags_ == kConstStringFlag) { + flags_ = rhs.flags_; + data_ = *reinterpret_cast(&rhs.data_); + } else { + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + } + break; + default: // kNumberType, kTrueType, kFalseType, kNullType + flags_ = rhs.flags_; + data_ = *reinterpret_cast(&rhs.data_); + } +} + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h new file mode 100644 index 0000000..9a93b38 --- /dev/null +++ b/include/rapidjson/encodedstream.h @@ -0,0 +1,261 @@ +// 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 "rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#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_; +}; + +//! Output byte stream wrapper with 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, 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); } + Ch Take() { RAPIDJSON_ASSERT(false); } + 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 = (const unsigned char *)is_->Peek4(); + if (!c) + return; + + unsigned bom = 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 InputByteStream 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); } + Ch Take() { RAPIDJSON_ASSERT(false); } + 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 __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h new file mode 100644 index 0000000..bc3cd81 --- /dev/null +++ b/include/rapidjson/encodings.h @@ -0,0 +1,625 @@ +// 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 bool Decode(InputStream& is, unsigned* codepoint) { +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu) +#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = (unsigned char)c; + return true; + } + + unsigned char type = GetRange((unsigned char)c); + *codepoint = (0xFF >> type) & (unsigned char)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((unsigned char)c) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c; + COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange((unsigned char)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); + Ch c = Take(is); + if ((unsigned char)c != 0xEFu) return c; + c = is.Take(); + if ((unsigned char)c != 0xBBu) return c; + c = is.Take(); + if ((unsigned char)c != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return is.Take(); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xEFu); os.Put(0xBBu); os.Put(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 bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = c; + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (c & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (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); + Ch c; + os.Put(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 (unsigned short)c == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take(); + c |= (unsigned char)is.Take() << 8; + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFFu); os.Put(0xFEu); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(c & 0xFFu); + os.Put((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 (unsigned short)c == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take(); + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFEu); os.Put(0xFFu); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put((c >> 8) & 0xFFu); + os.Put(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 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 (unsigned)c == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take(); + c |= (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take() << 16; + c |= (unsigned char)is.Take() << 24; + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(c & 0xFFu); + os.Put((c >> 8) & 0xFFu); + os.Put((c >> 16) & 0xFFu); + os.Put((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 (unsigned)c == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take() << 24; + c |= (unsigned char)is.Take() << 16; + c |= (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take(); + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put((c >> 24) & 0xFFu); + os.Put((c >> 16) & 0xFFu); + os.Put((c >> 8) & 0xFFu); + os.Put(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 bool Decode(InputStream& is, unsigned* codepoint) { + unsigned char c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + unsigned char c = is.Take(); + os.Put(c); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + Ch c = Take(is); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return 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 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; + } + + //! 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. + } +}; + +//! 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 Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(_MSV_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h new file mode 100644 index 0000000..d5f9caa --- /dev/null +++ b/include/rapidjson/error/en.h @@ -0,0 +1,65 @@ +// 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" + +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 follow 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 + +#endif // RAPIDJSON_ERROR_EN_H__ diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h new file mode 100644 index 0000000..f9094fb --- /dev/null +++ b/include/rapidjson/error/error.h @@ -0,0 +1,146 @@ +// 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" + +/*! \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 { + + //! 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 + +#endif // RAPIDJSON_ERROR_ERROR_H__ diff --git a/include/rapidjson/filereadstream.h b/include/rapidjson/filereadstream.h new file mode 100644 index 0000000..78ee403 --- /dev/null +++ b/include/rapidjson/filereadstream.h @@ -0,0 +1,88 @@ +// 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 "rapidjson.h" +#include + +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 + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/filewritestream.h b/include/rapidjson/filewritestream.h new file mode 100644 index 0000000..31223b8 --- /dev/null +++ b/include/rapidjson/filewritestream.h @@ -0,0 +1,91 @@ +// 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 "rapidjson.h" +#include + +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_) { + fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + 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 + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h new file mode 100755 index 0000000..99a30ac --- /dev/null +++ b/include/rapidjson/internal/biginteger.h @@ -0,0 +1,280 @@ +// 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 +#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=(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 * 10 + (*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/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h new file mode 100644 index 0000000..3b6c423 --- /dev/null +++ b/include/rapidjson/internal/diyfp.h @@ -0,0 +1,247 @@ +// 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) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +struct DiyFp { + DiyFp() {} + + 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 = (exp + 348) / 8; + *outExp = -348 + index * 8; + return GetCachedPowerByIndex(index); + } + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h new file mode 100644 index 0000000..2d8d2e4 --- /dev/null +++ b/include/rapidjson/internal/dtoa.h @@ -0,0 +1,217 @@ +// 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++) +#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); + int 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; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); + 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) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (length <= kk && 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], length - kk); + buffer[kk] = '.'; + 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], length); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + return &buffer[length + offset]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], length - 1); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer) { + 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); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h new file mode 100644 index 0000000..e3f0336 --- /dev/null +++ b/include/rapidjson/internal/ieee754.h @@ -0,0 +1,77 @@ +// 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 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 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/include/rapidjson/internal/itoa.h b/include/rapidjson/internal/itoa.h new file mode 100644 index 0000000..01a4e7e --- /dev/null +++ b/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/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h new file mode 100644 index 0000000..2daad96 --- /dev/null +++ b/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/include/rapidjson/internal/pow10.h b/include/rapidjson/internal/pow10.h new file mode 100644 index 0000000..1d2dff0 --- /dev/null +++ b/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/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h new file mode 100644 index 0000000..bb31cc0 --- /dev/null +++ b/include/rapidjson/internal/stack.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. + +#ifndef RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../rapidjson.h" + +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) { + RAPIDJSON_ASSERT(stackCapacity > 0); + } + +#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 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 T* Push(size_t count = 1) { + // Expand the stack if needed + if (stackTop_ + sizeof(T) * count >= stackEnd_) + Expand(count); + + 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 + T* Bottom() { return (T*)stack_; } + + Allocator& GetAllocator() { 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_ = (char*)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 + +#endif // RAPIDJSON_STACK_H_ diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h new file mode 100644 index 0000000..f6c99db --- /dev/null +++ b/include/rapidjson/internal/strfunc.h @@ -0,0 +1,39 @@ +// 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 "../rapidjson.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); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h new file mode 100644 index 0000000..ace65f6 --- /dev/null +++ b/include/rapidjson/internal/strtod.h @@ -0,0 +1,270 @@ +// 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 "../rapidjson.h" +#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(dS_Exp5) <<= dS_Exp2; + + BigInteger bS(bInt); + bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2; + + BigInteger hS(1); + hS.MultiplyPow5(hS_Exp5) <<= 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 * 10 + (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; + int error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + const int dExp = (int)decimalPosition - (int)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 + adjustment > 19) // 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 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + 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 + error) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - error >= precisionBits || precisionBits >= halfWay + 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 = (int)decimalPosition - (int)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 ((int)length > kMaxDecimalDigit) { + int delta = (int(length) - kMaxDecimalDigit); + exp += delta; + decimalPosition -= 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/include/rapidjson/memorybuffer.h b/include/rapidjson/memorybuffer.h new file mode 100644 index 0000000..2484b21 --- /dev/null +++ b/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 "rapidjson.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/include/rapidjson/memorystream.h b/include/rapidjson/memorystream.h new file mode 100644 index 0000000..99feae5 --- /dev/null +++ b/include/rapidjson/memorystream.h @@ -0,0 +1,61 @@ +// 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 "rapidjson.h" + +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 (src_ == end_) ? '\0' : *src_; } + Ch Take() { return (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 + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/msinttypes/inttypes.h b/include/rapidjson/msinttypes/inttypes.h new file mode 100644 index 0000000..1811128 --- /dev/null +++ b/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/include/rapidjson/msinttypes/stdint.h b/include/rapidjson/msinttypes/stdint.h new file mode 100644 index 0000000..a26fff4 --- /dev/null +++ b/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 should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#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/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h new file mode 100644 index 0000000..fff8886 --- /dev/null +++ b/include/rapidjson/prettywriter.h @@ -0,0 +1,207 @@ +// 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 + +//! 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> +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. + */ + PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, 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; + } + + /*! @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 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); } + + 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) { + 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)); } + + //@} +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 + Base::os_->Put('\n'); + } + else + 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_, indentChar_, count); + } + + Ch indentChar_; + unsigned indentCharCount_; + +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/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h new file mode 100644 index 0000000..f5d5630 --- /dev/null +++ b/include/rapidjson/rapidjson.h @@ -0,0 +1,654 @@ +// 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 0 +#define RAPIDJSON_PATCH_VERSION 2 +#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_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 +#ifdef _MSC_VER +#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(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(_WIN64) +#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. User can customize by defining the RAPIDJSON_ALIGN function macro., +*/ +#ifndef RAPIDJSON_ALIGN +#if RAPIDJSON_64BIT == 1 +#define RAPIDJSON_ALIGN(x) ((x + 7u) & ~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_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 +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +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 +//!@endcond + +/*! \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 + +/////////////////////////////////////////////////////////////////////////////// +// 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__) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) && \ + (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#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 + +//!@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 + +/////////////////////////////////////////////////////////////////////////////// +// Allocators and Encodings + +#include "allocators.h" +#include "encodings.h" + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +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 }; +}; + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + for (size_t i = 0; i < n; i++) + stream.Put(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; + +/////////////////////////////////////////////////////////////////////////////// +// Type + +//! 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/include/rapidjson/reader.h b/include/rapidjson/reader.h new file mode 100644 index 0000000..be0d9fb --- /dev/null +++ b/include/rapidjson/reader.h @@ -0,0 +1,1452 @@ +// 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 "rapidjson.h" +#include "encodings.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" + +#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 __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 (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). + 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); + 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(); } + 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); + + while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + s.Take(); +} + +#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) & ~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_load_si128((const __m128i *)&whitespace[0]); + + for (;; p += 16) { + const __m128i s = _mm_load_si128((const __m128i *)p); + const unsigned 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 + } + } +} + +#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) & ~15); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + static const char whitespaces[4][17] = { + " ", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"}; + + const __m128i w0 = _mm_loadu_si128((const __m128i *)&whitespaces[0][0]); + const __m128i w1 = _mm_loadu_si128((const __m128i *)&whitespaces[1][0]); + const __m128i w2 = _mm_loadu_si128((const __m128i *)&whitespaces[2][0]); + const __m128i w3 = _mm_loadu_si128((const __m128i *)&whitespaces[3][0]); + + for (;; p += 16) { + const __m128i s = _mm_load_si128((const __m128i *)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 = (unsigned short)~_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 + } + } +} + +#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_); +} +#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); + + SkipWhitespace(is); + + if (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)) { + SkipWhitespace(is); + + if (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&); + }; + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (!handler.StartObject()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespace(is); + + if (is.Peek() == '}') { + is.Take(); + if (!handler.EndObject(0)) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (is.Peek() != '"') + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespace(is); + + if (is.Take() != ':') + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespace(is); + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespace(is); + + ++memberCount; + + switch (is.Take()) { + case ',': SkipWhitespace(is); break; + case '}': + if (!handler.EndObject(memberCount)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (!handler.StartArray()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespace(is); + + if (is.Peek() == ']') { + is.Take(); + if (!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; + SkipWhitespace(is); + + switch (is.Take()) { + case ',': SkipWhitespace(is); break; + case ']': + if (!handler.EndArray(elementCount)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') { + if (!handler.Null()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') { + if (!handler.Bool(true)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') { + if (!handler.Bool(false)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Take(); + 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, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + } + 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_; + } + 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); + + 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 = (typename TargetEncoding::Ch*)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 (!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 + + RAPIDJSON_ASSERT(is.Peek() == '\"'); + is.Take(); // Skip '\"' + + for (;;) { + Ch c = is.Peek(); + if (c == '\\') { // Escape + is.Take(); + Ch e = is.Take(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) { + os.Put(escape[(unsigned char)e]); + } + else if (e == 'u') { // Unicode + unsigned codepoint = ParseHex4(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { + // Handle UTF-16 surrogate pair + if (is.Take() != '\\' || is.Take() != 'u') + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + unsigned codepoint2 = ParseHex4(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + } + else if (c == '"') { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); + else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + else { + if (parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + } + } + + template + class NumberStream; + + template + class NumberStream { + public: + 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(); } + 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) : NumberStream(reader, is), stackStream(reader.stack_) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put((char)Base::is.Peek()); + return Base::is.Take(); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + // Parse minus + bool minus = false; + if (s.Peek() == '-') { + minus = true; + s.Take(); + } + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (s.Peek() == '0') { + i = 0; + s.TakePush(); + } + else if (s.Peek() >= '1' && s.Peek() <= '9') { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i >= 214748364) { // 2^31 = 2147483648 + if (i != 214748364 || s.Peek() > '8') { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i >= 429496729) { // 2^32 - 1 = 4294967295 + if (i != 429496729 || s.Peek() > '5') { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + double d = 0.0; + if (use64bit) { + if (minus) + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC)) // 2^63 = 9223372036854775808 + if (i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8') { + d = i64; + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) // 2^64 - 1 = 18446744073709551615 + if (i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5') { + d = i64; + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (d >= 1.7976931348623157e307) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (s.Peek() == '.') { + s.Take(); + decimalPosition = s.Length(); + + if (!(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 (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 = (double)i64; +#else + // Use double to store significand in 32-bit architecture + d = use64bit ? (double)i64 : (double)i; +#endif + useDouble = true; + } + + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (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 (s.Peek() == 'e' || s.Peek() == 'E') { + if (!useDouble) { + d = use64bit ? i64 : i; + useDouble = true; + } + s.Take(); + + bool expMinus = false; + if (s.Peek() == '+') + s.Take(); + else if (s.Peek() == '-') { + s.Take(); + expMinus = true; + } + + if (s.Peek() >= '0' && s.Peek() <= '9') { + exp = s.Take() - '0'; + if (expMinus) { + while (s.Peek() >= '0' && s.Peek() <= '9') { + exp = exp * 10 + (s.Take() - '0'); + if (exp >= 214748364) { // Issue #313: prevent overflow exponent + while (s.Peek() >= '0' && s.Peek() <= '9') // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (s.Peek() >= '0' && s.Peek() <= '9') { + exp = exp * 10 + (s.Take() - '0'); + if (exp > maxExp) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + 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 (use64bit) { + if (minus) + cont = handler.Int64(-(int64_t)i64); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(-(int)i); + else + cont = handler.Uint(i); + } + } + if (!cont) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // 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); + } + } + + // 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, + + cIterativeParsingStateCount + }; + + // 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 || unsigned(c) < 256) + return (Token)tokenMap[(unsigned char)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 + IterativeParsingErrorState, // 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) + IterativeParsingErrorState, // 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 (IterativeParsingState)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: + { + // 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: + { + // 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()); + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + } + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespace(is); + 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; + + SkipWhitespace(is); + } + + // 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 __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/include/rapidjson/stringbuffer.h b/include/rapidjson/stringbuffer.h new file mode 100644 index 0000000..e9be849 --- /dev/null +++ b/include/rapidjson/stringbuffer.h @@ -0,0 +1,93 @@ +// 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 "rapidjson.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +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 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); + } + Ch* Push(size_t count) { return stack_.template Push(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; + +//! 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 + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h new file mode 100644 index 0000000..40cdb35 --- /dev/null +++ b/include/rapidjson/writer.h @@ -0,0 +1,395 @@ +// 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 "rapidjson.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! 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> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + //! 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)), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), 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(); + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return WriteNull(); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } + bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } + bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } + bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return 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 WriteDouble(d); } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kStringType); + return 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); + bool ret = WriteEndObject(); + if (level_stack_.Empty()) // end of json text + os_->Flush(); + return ret; + } + + 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); + bool ret = WriteEndArray(); + if (level_stack_.Empty()) // end of json text + os_->Flush(); + return ret; + } + //@} + + /*! @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)); } + + //@} + +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() { + os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e'); + } + else { + os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + for (char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteDouble(double d) { + char buffer[25]; + char* end = internal::dtoa(d, buffer); + for (char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const char 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 + }; + + os_->Put('\"'); + GenericStringStream is(str); + while (is.Tell() < length) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + os_->Put('\\'); + os_->Put('u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + os_->Put(hexDigits[(codepoint >> 12) & 15]); + os_->Put(hexDigits[(codepoint >> 8) & 15]); + os_->Put(hexDigits[(codepoint >> 4) & 15]); + os_->Put(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; + os_->Put(hexDigits[(lead >> 12) & 15]); + os_->Put(hexDigits[(lead >> 8) & 15]); + os_->Put(hexDigits[(lead >> 4) & 15]); + os_->Put(hexDigits[(lead ) & 15]); + os_->Put('\\'); + os_->Put('u'); + os_->Put(hexDigits[(trail >> 12) & 15]); + os_->Put(hexDigits[(trail >> 8) & 15]); + os_->Put(hexDigits[(trail >> 4) & 15]); + os_->Put(hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { + is.Take(); + os_->Put('\\'); + os_->Put(escape[(unsigned char)c]); + if (escape[(unsigned char)c] == 'u') { + os_->Put('0'); + os_->Put('0'); + os_->Put(hexDigits[(unsigned char)c >> 4]); + os_->Put(hexDigits[(unsigned char)c & 0xF]); + } + } + else + if (!Transcoder::Transcode(is, *os_)) + return false; + } + os_->Put('\"'); + return true; + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + void Prefix(Type type) { + (void)type; + if (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; + } + } + + OutputStream* os_; + internal::Stack level_stack_; + 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(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(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(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(20 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer); + os_->Pop(25 - (end - buffer)); + return true; +} + +RAPIDJSON_NAMESPACE_END + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ From 82bc44129122cf757dbdc9361c0516dd1b4de93b Mon Sep 17 00:00:00 2001 From: bokse001 Date: Thu, 27 Oct 2016 14:47:51 +0200 Subject: [PATCH 24/24] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 32dc233..8963732 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Pictures running daemon on Raspberry PI with uputronics Raspberry Pi+ LoRa(TM) Expansion Board -Dual Channel GW TTN +Dual Channel GW TTN running daemon on Raspberry PI with LoRasPI shield @@ -200,4 +200,4 @@ License The source files in this repository are made available under the Eclipse Public License v1.0, except: base64 implementation, that has been copied from the Semtech Packet Forwarder; -RapidJSON, licensed under the MIT License. \ No newline at end of file +RapidJSON, licensed under the MIT License.