From: joey Date: Tue, 7 May 2013 10:10:54 +0000 (+0800) Subject: testkit-stub v2.1 initial version X-Git-Tag: upstream/1.0~5 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f3d6a11a95538e313ad0f648175fafc5b5ee3d24;p=test%2Ftools%2Ftestkit-stub.git testkit-stub v2.1 initial version --- diff --git a/CommandLineBuild/build_data b/CommandLineBuild/build_data new file mode 100644 index 0000000..b09603f --- /dev/null +++ b/CommandLineBuild/build_data @@ -0,0 +1,9 @@ +target:emulator:tizen-emulator-2.0.cpp_llvm31.i386.cpp.app +target:device:tizen-device-2.0.cpp_llvm31.armel.cpp.app +target:emulator:includepath:-I/home/danny/tizen-sdk/tools/empty/inc -I/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-emulator-2.0.cpp/usr/include -I/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-emulator-2.0.cpp/usr/include/libxml2 -I/home/danny/tizen-sdk/library -I/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-emulator-2.0.cpp/usr/include/osp +target:device:includepath:-I/home/danny/tizen-sdk/tools/empty/inc -I/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-device-2.0.cpp/usr/include -I/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-device-2.0.cpp/usr/include/libxml2 -I/home/danny/tizen-sdk/library -I/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-device-2.0.cpp/usr/include/osp +target:emulator:cflags:-target i386-tizen-linux-gnueabi -gcc-toolchain /home/danny/tizen-sdk/tools/smart-build-interface/../i386-linux-gnueabi-gcc-4.5/ -ccc-gcc-name i386-linux-gnueabi-g++ -march=i386 -Wno-gnu -fPIE --sysroot=/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-emulator-2.0.cpp +target:device:cflags:-target arm-tizen-linux-gnueabi -gcc-toolchain /home/danny/tizen-sdk/tools/smart-build-interface/../arm-linux-gnueabi-gcc-4.5/ -ccc-gcc-name arm-linux-gnueabi-g++ -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mtune=cortex-a8 -Wno-gnu -fPIE --sysroot=/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-device-2.0.cpp +target:emulator:lflags:-target i386-tizen-linux-gnueabi -gcc-toolchain /home/danny/tizen-sdk/tools/smart-build-interface/../i386-linux-gnueabi-gcc-4.5/ -ccc-gcc-name i386-linux-gnueabi-g++ -march=i386 -Xlinker --as-needed -pie -lpthread --sysroot=/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-emulator-2.0.cpp -L/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-emulator-2.0.cpp/usr/lib -L/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-emulator-2.0.cpp/usr/lib/osp -losp-appfw -losp-uifw -losp-image -losp-json -losp-ime -losp-net -lpthread -losp-content -losp-locations -losp-telephony -losp-uix -losp-media -losp-messaging -losp-web -losp-social -losp-wifi -losp-bluetooth -losp-nfc -losp-face -losp-speech-tts -losp-speech-stt -losp-shell -losp-shell-core -lxml2 +target:device:lflags:-target arm-tizen-linux-gnueabi -gcc-toolchain /home/danny/tizen-sdk/tools/smart-build-interface/../arm-linux-gnueabi-gcc-4.5/ -ccc-gcc-name arm-linux-gnueabi-g++ -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mtune=cortex-a8 -Xlinker --as-needed -pie -lpthread --sysroot=/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-device-2.0.cpp -L/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-device-2.0.cpp/usr/lib -L/home/danny/tizen-sdk/platforms/tizen2.0/rootstraps/tizen-device-2.0.cpp/usr/lib/osp -losp-appfw -losp-uifw -losp-image -losp-json -losp-ime -losp-net -lpthread -losp-content -losp-locations -losp-telephony -losp-uix -losp-media -losp-messaging -losp-web -losp-social -losp-wifi -losp-bluetooth -losp-nfc -losp-face -losp-speech-tts -losp-shell -losp-shell-core -losp-speech-stt -lxml2 + diff --git a/CommandLineBuild/makefile b/CommandLineBuild/makefile new file mode 100644 index 0000000..218cbf3 --- /dev/null +++ b/CommandLineBuild/makefile @@ -0,0 +1,135 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +-include ../makefile.init +INCLUDES = -I ../include + +RM := rm -rf + +# All of the sources participating in the build are defined here +# -include sources.mk +O_SRCS := +PO_SRCS := +CPP_SRCS := +C_UPPER_SRCS := +C_SRCS := +S_UPPER_SRCS := +OBJ_SRCS := +EDC_SRCS := +ASM_SRCS := +CXX_SRCS := +C++_SRCS := +CC_SRCS := +MO_FILES := +OBJS := +C++_DEPS := +C_DEPS := +CC_DEPS := +EDJ_FILES := +CPP_DEPS := +EXECUTABLES := +C_UPPER_DEPS := +CXX_DEPS := + +# Every subdirectory with source files must be described here +SUBDIRS := \ +src \ + +# -include src/subdir.mk +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ +../src/main/main.cpp \ +../src/comfun.cpp \ +../src/testcase.cpp \ +../src/httpserver.cpp \ +../src/json/json_reader.cpp \ +../src/json/json_value.cpp \ +../src/json/json_writer.cpp + + +OBJS += \ +./src/main/main.o \ +./src/comfun.o \ +./src/testcase.o \ +./src/httpserver.o \ +./src/json/json_reader.o \ +./src/json/json_value.o \ +./src/json/json_writer.o + + +CPP_DEPS += \ +./src/main/main.d \ +./src/comfun.d \ +./src/testcase.d \ +./src/httpserver.d \ +./src/json/json_reader.d \ +./src/json/json_value.d \ +./src/json/json_writer.d + + +# Each subdirectory must supply rules for building sources it contributes +src/%.o: ../src/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: C++ Compiler' + $(CC) -I"pch" -D_DEBUG -O0 -g3 -Wall -c -fmessage-length=0 $(INCLUDE_PATH) $(INCLUDES) $(CFLAG) -D_APP_LOG -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + +# -include objects.mk +USER_OBJS := + +LIBS := + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(strip $(C++_DEPS)),) +-include $(C++_DEPS) +endif +ifneq ($(strip $(C_DEPS)),) +-include $(C_DEPS) +endif +ifneq ($(strip $(CC_DEPS)),) +-include $(CC_DEPS) +endif +ifneq ($(strip $(CPP_DEPS)),) +-include $(CPP_DEPS) +endif +ifneq ($(strip $(CXX_DEPS)),) +-include $(CXX_DEPS) +endif +ifneq ($(strip $(C_UPPER_DEPS)),) +-include $(C_UPPER_DEPS) +endif +endif + +-include ../makefile.defs + +# Add inputs and outputs from these tool invocations to the build variables + +# All Target +all: httpserver secondary-outputs + +init: + mkdir -p src/json + mkdir -p src/main + +# Tool invocations +httpserver: init $(OBJS) $(USER_OBJS) + @echo 'Building target: $@' + @echo 'Invoking: C++ Linker' + $(CC) -o"httpserver" $(OBJS) $(USER_OBJS) $(LIBS) $(LFLAG) + @echo 'Finished building target: $@' + @echo ' ' + +# Other Targets +clean: + -$(RM) src httpserver + +secondary-outputs: $(MO_FILES) $(EDJ_FILES) + +.PHONY: all clean dependents +.SECONDARY: + +-include ../makefile.targets + diff --git a/README.md b/README.md index e69de29..2ffb37f 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,17 @@ +TCT-stub +======== + +Intel Tizen Compatibility Test Stub + +to build ARM version locally(without OBS), please make sure + +1, install tizen2.0 SDK with all component selected, + +2, replace all "/home/danny/" in build_data to your own home path, such as "/home/wl/" + +3, run native-make -t device in CommandLineBuild folder. + + +to build unit test, just run "make ut" in root folder of the project. then run valgrind to check memory leak. + +valgrind --tool=memcheck --leak-check=yes --track-origins=yes -v ./ut \ No newline at end of file diff --git a/include/comfun.h b/include/comfun.h new file mode 100644 index 0000000..4540ae3 --- /dev/null +++ b/include/comfun.h @@ -0,0 +1,16 @@ +#include +#include +#include +#include +using namespace std; +class ComFun { +public: + ComFun(); + virtual ~ComFun(); +public: + static char CharToInt(char ch); + static char StrToBin(char *str); + static char* UrlDecode(const char *str); + static std::vector split(std::string str, std::string pattern); +}; + diff --git a/include/httpserver.h b/include/httpserver.h new file mode 100644 index 0000000..21f1899 --- /dev/null +++ b/include/httpserver.h @@ -0,0 +1,95 @@ +#include +#include +#include +#include "testcase.h" + +#include +using namespace std; + +void timer_handler(int signum); + +typedef struct HttpRequest { + string method; // request type + string path; // request path + string content; // request content + int contentlength; // length of request length + int contenttype; // response content type + string prefix; + int responsecode; //response code +} HR; + +class HttpServer { +public: + HttpServer(); + virtual ~HttpServer(); +public: + void StartUp(); + void processpost(int s, struct HttpRequest *prequest); + void sendresponse(int s, int code, struct HttpRequest *prequest, + string content); + int sendsegment(int s, string buffer); + void parse_json_str(string jsonstr); + int getrequest(string requestbuf, struct HttpRequest *prequest); + bool get_auto_case(string content, string *type); + void checkResult(TestCase* testcase); + void killAllWidget(); + void start_client(); + bool run_cmd(string cmdString, string expectString, bool showcmdAnyway); + void print_info_string(string case_id); + void find_purpose(struct HttpRequest *prequest, bool auto_test); + void getCurrentTime(); + void cancel_time_check(); + void set_timer(int timeout_value); + void getAllWidget(); + + struct sigaction sa; + struct itimerval timer; + int gIsRun; + int clientsocket; + + int gServerStatus; + + string m_exeType; //auto;manual + TestCase *m_test_cases; //the case array + + //block + int m_totalBlocks; + int m_current_block_index; + int m_totalcaseCount; + int m_total_case_index; //current case index in set + int m_block_case_index; //current case index in block + int m_block_case_count; //case count in block + bool m_block_finished; + bool m_set_finished; + bool m_server_checked; + + Json::Value m_capability; + //TestStatus + int m_timeout_count; // continusously time out count + + int m_killing_widget; + + std::vector m_widgets; // store all the widgets short name + + string m_running_session; + + string m_last_auto_result; + + int m_failto_launch; // time of fail to launch + + bool m_rerun; // true when re-run a case + + //some variables get from cmd line + bool g_show_log; + string g_port; + string g_hide_status; + string g_test_suite; + string g_launch_cmd; + string g_kill_cmd; + string g_exe_sequence; + string g_enable_memory_collection; + string g_launcher;//lancher name:wrt-launcher/browser + bool g_run_wiget;//whether run on the device with wiget + + ofstream outputFile; +}; diff --git a/include/json/autolink.h b/include/json/autolink.h new file mode 100644 index 0000000..37c9258 --- /dev/null +++ b/include/json/autolink.h @@ -0,0 +1,19 @@ +#ifndef JSON_AUTOLINK_H_INCLUDED +# define JSON_AUTOLINK_H_INCLUDED + +# include "config.h" + +# ifdef JSON_IN_CPPTL +# include +# endif + +# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL) +# define CPPTL_AUTOLINK_NAME "json" +# undef CPPTL_AUTOLINK_DLL +# ifdef JSON_DLL +# define CPPTL_AUTOLINK_DLL +# endif +# include "autolink.h" +# endif + +#endif // JSON_AUTOLINK_H_INCLUDED diff --git a/include/json/config.h b/include/json/config.h new file mode 100644 index 0000000..5d334cb --- /dev/null +++ b/include/json/config.h @@ -0,0 +1,43 @@ +#ifndef JSON_CONFIG_H_INCLUDED +# define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 +/// If defined, indicates that Json specific container should be used +/// (hash table & simple deque container with customizable allocator). +/// THIS FEATURE IS STILL EXPERIMENTAL! +//# define JSON_VALUE_USE_INTERNAL_MAP 1 +/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. +/// The memory pools allocator used optimization (initializing Value and ValueInternalLink +/// as if it was a POD) that may cause some validation tool to report errors. +/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. +//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 + +/// If defined, indicates that Json use exception to report invalid type manipulation +/// instead of C assert macro. +# define JSON_USE_EXCEPTION 1 + +# ifdef JSON_IN_CPPTL +# include +# ifndef JSON_USE_CPPTL +# define JSON_USE_CPPTL 1 +# endif +# endif + +# ifdef JSON_IN_CPPTL +# define JSON_API CPPTL_API +# elif defined(JSON_DLL_BUILD) +# define JSON_API __declspec(dllexport) +# elif defined(JSON_DLL) +# define JSON_API __declspec(dllimport) +# else +# define JSON_API +# endif + +#endif // JSON_CONFIG_H_INCLUDED diff --git a/include/json/features.h b/include/json/features.h new file mode 100644 index 0000000..68e473c --- /dev/null +++ b/include/json/features.h @@ -0,0 +1,41 @@ +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +# define CPPTL_JSON_FEATURES_H_INCLUDED + +# include "forwards.h" + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c false. + bool strictRoot_; +}; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff --git a/include/json/forwards.h b/include/json/forwards.h new file mode 100644 index 0000000..4d46633 --- /dev/null +++ b/include/json/forwards.h @@ -0,0 +1,38 @@ +#ifndef JSON_FORWARDS_H_INCLUDED +# define JSON_FORWARDS_H_INCLUDED + +# include "config.h" + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef int Int; +typedef unsigned int UInt; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; +#ifdef JSON_VALUE_USE_INTERNAL_MAP +class ValueAllocator; +class ValueMapAllocator; +class ValueInternalLink; +class ValueInternalArray; +class ValueInternalMap; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP +} + // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED diff --git a/include/json/json.h b/include/json/json.h new file mode 100644 index 0000000..c71ed65 --- /dev/null +++ b/include/json/json.h @@ -0,0 +1,10 @@ +#ifndef JSON_JSON_H_INCLUDED +# define JSON_JSON_H_INCLUDED + +# include "autolink.h" +# include "value.h" +# include "reader.h" +# include "writer.h" +# include "features.h" + +#endif // JSON_JSON_H_INCLUDED diff --git a/include/json/reader.h b/include/json/reader.h new file mode 100644 index 0000000..6020c42 --- /dev/null +++ b/include/json/reader.h @@ -0,0 +1,177 @@ +#ifndef CPPTL_JSON_READER_H_INCLUDED +# define CPPTL_JSON_READER_H_INCLUDED + +# include "features.h" +# include "value.h" +# include +# include +# include +# include + +namespace Json { + +/** \brief Unserialize a JSON document into a Value. + * + */ +class JSON_API Reader { +public: + typedef char Char; + typedef const Char *Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features &features); + + /** \brief Read a Value from a JSON document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse(const std::string &document, Value &root, bool collectComments = + true); + + /** \brief Read a Value from a JSON document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse(const char *beginDoc, const char *endDoc, Value &root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(std::istream &is, Value &root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + */ + std::string getFormatedErrorMessages() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool expectToken(TokenType type, Token &token, const char *message); + bool readToken(Token &token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token &token); + bool readArray(Token &token); + bool decodeNumber(Token &token); + bool decodeString(Token &token); + bool decodeString(Token &token, std::string &decoded); + bool decodeDouble(Token &token); + bool decodeUnicodeCodePoint(Token &token, Location ¤t, Location end, + unsigned int &unicode); + bool decodeUnicodeEscapeSequence(Token &token, Location ¤t, + Location end, unsigned int &unicode); + bool addError(const std::string &message, Token &token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string &message, Token &token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value ¤tValue(); + Char getNextChar(); + void getLocationLineAndColumn(Location location, int &line, + int &column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token &token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value *lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; +}; + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() + */ +std::istream& operator>>(std::istream&, Value&); + +} // namespace Json + +#endif // CPPTL_JSON_READER_H_INCLUDED diff --git a/include/json/value.h b/include/json/value.h new file mode 100644 index 0000000..736cce5 --- /dev/null +++ b/include/json/value.h @@ -0,0 +1,1013 @@ +#ifndef CPPTL_JSON_H_INCLUDED +# define CPPTL_JSON_H_INCLUDED + +# include "forwards.h" +# include +# include + +# ifndef JSON_USE_CPPTL_SMALLMAP +# include +# else +# include +# endif +# ifdef JSON_USE_CPPTL +# include +# endif + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for root value) + numberOfCommentPlacement +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char *czstring) : + str_(czstring) { + } + + operator const char *() const { + return str_; + } + + const char *c_str() const { + return str_; + } + +private: + const char *str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. + * Non const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resize and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtanis default value in the case the required element + * does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + */ +class JSON_API Value { + friend class ValueIteratorBase; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + friend class ValueInternalLink; + friend class ValueInternalMap; +# endif +public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; + typedef UInt ArrayIndex; + + static const Value null; + static const Int minInt; + static const Int maxInt; + static const UInt maxUInt; + +private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +# ifndef JSON_VALUE_USE_INTERNAL_MAP + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, duplicate, duplicateOnCopy + }; + CZString(int index); + CZString(const char *cstr, DuplicationPolicy allocate); + CZString(const CZString &other); + ~CZString(); + CZString &operator =(const CZString &other); + bool operator<(const CZString &other) const; + bool operator==(const CZString &other) const; + int index() const; + const char *c_str() const; + bool isStaticString() const; + private: + void swap(CZString &other); + const char *cstr_; + int index_; + }; + +public: +# ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +# else + typedef CppTL::SmallMap ObjectValues; +# endif // ifndef JSON_USE_CPPTL_SMALLMAP +# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. + This is useful since clear() and resize() will not alter types. + + Examples: + \code + Json::Value null_value; // null + Json::Value arr_value(Json::arrayValue); // [] + Json::Value obj_value(Json::objectValue); // {} + \endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); + Value(double value); + Value(const char *value); + Value(const char *beginValue, const char *endValue); + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * \endcode + */ + Value(const StaticString &value); + Value(const std::string &value); +# ifdef JSON_USE_CPPTL + Value( const CppTL::ConstString &value ); +# endif + Value(bool value); + Value(const Value &other); + ~Value(); + + Value &operator=(const Value &other); + /// Swap values. + /// \note Currently, comments are intentionally not swapped, for + /// both logic and efficiency. + void swap(Value &other); + + ValueType type() const; + + bool operator <(const Value &other) const; + bool operator <=(const Value &other) const; + bool operator >=(const Value &other) const; + bool operator >(const Value &other) const; + + bool operator ==(const Value &other) const; + bool operator !=(const Value &other) const; + + int compare(const Value &other); + + const char *asCString() const; + std::string asString() const; +# ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +# endif + Int asInt() const; + UInt asUInt() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isUInt() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + UInt size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(UInt size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[](UInt index); + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[](UInt index) const; + /// If the array contains at least index+1 elements, returns the element value, + /// otherwise returns defaultValue. + Value get(UInt index, const Value &defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(UInt index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value &append(const Value &value); + + /// Access an object value by name, create a null member if it does not exist. + Value &operator[](const char *key); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[](const char *key) const; + /// Access an object value by name, create a null member if it does not exist. + Value &operator[](const std::string &key); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[](const std::string &key) const; + /** \brief Access an object value by name, create a null member if it does not exist. + + * If the object as no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value &operator[](const StaticString &key); +# ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const CppTL::ConstString &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const CppTL::ConstString &key ) const; +# endif + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const char *key, const Value &defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const std::string &key, const Value &defaultValue) const; +# ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const CppTL::ConstString &key, + const Value &defaultValue ) const; +# endif + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + Value removeMember(const char* key); + /// Same as removeMember(const char*) + Value removeMember(const std::string &key); + + /// Return true if the object has a member named key. + bool isMember(const char *key) const; + /// Return true if the object has a member named key. + bool isMember(const std::string &key) const; +# ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember( const CppTL::ConstString &key ) const; +# endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + +//# ifdef JSON_USE_CPPTL +// EnumMemberNames enumMemberNames() const; +// EnumValues enumValues() const; +//# endif + + /// Comments must be //... or /* ... */ + void setComment(const char *comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const std::string &comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + std::string getComment(CommentPlacement placement) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + +private: + Value &resolveReference(const char *key, bool isStatic); + +# ifdef JSON_VALUE_USE_INTERNAL_MAP + inline bool isItemAvailable() const + { + return itemIsUsed_ == 0; + } + + inline void setItemUsed( bool isUsed = true ) + { + itemIsUsed_ = isUsed ? 1 : 0; + } + + inline bool isMemberNameStatic() const + { + return memberNameIsStatic_ == 0; + } + + inline void setMemberNameIsStatic( bool isStatic ) + { + memberNameIsStatic_ = isStatic ? 1 : 0; + } +# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP +private: + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char *text); + + char *comment_; + }; + + //struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + Int int_; + UInt uint_; + double real_; + bool bool_; + char *string_; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + ValueInternalArray *array_; + ValueInternalMap *map_; +#else + ObjectValues *map_; +# endif + } value_; + ValueType type_ :8; + int allocated_ :1; // Notes: if declared as bool, bitfield is useless. +# ifdef JSON_VALUE_USE_INTERNAL_MAP + unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. + int memberNameIsStatic_ : 1;// used by the ValueInternalMap container. +# endif + CommentInfo *comments_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to access a node. + */ +class PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(UInt index); + PathArgument(const char *key); + PathArgument(const std::string &key); + +private: + enum Kind { + kindNone = 0, kindIndex, kindKey + }; + std::string key_; + UInt index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class Path { +public: + Path(const std::string &path, const PathArgument &a1 = PathArgument(), + const PathArgument &a2 = PathArgument(), const PathArgument &a3 = + PathArgument(), const PathArgument &a4 = PathArgument(), + const PathArgument &a5 = PathArgument()); + + const Value &resolve(const Value &root) const; + Value resolve(const Value &root, const Value &defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on the node. + Value &make(Value &root) const; + +private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath(const std::string &path, const InArgs &in); + void addPathInArg(const std::string &path, const InArgs &in, + InArgs::const_iterator &itInArg, PathArgument::Kind kind); + void invalidPath(const std::string &path, int location); + + Args args_; +}; + +/** \brief Experimental do not use: Allocator to customize member name and string value memory management done by Value. + * + * - makeMemberName() and releaseMemberName() are called to respectively duplicate and + * free an Json::objectValue member name. + * - duplicateStringValue() and releaseStringValue() are called similarly to + * duplicate and free a Json::stringValue value. + */ +class ValueAllocator { +public: + enum { + unknown = (unsigned) -1 + }; + + virtual ~ValueAllocator(); + + virtual char *makeMemberName(const char *memberName) = 0; + virtual void releaseMemberName(char *memberName) = 0; + virtual char *duplicateStringValue(const char *value, unsigned int length = + unknown) = 0; + virtual void releaseStringValue(char *value) = 0; +}; + +#ifdef JSON_VALUE_USE_INTERNAL_MAP +/** \brief Allocator to customize Value internal map. + * Below is an example of a simple implementation (default implementation actually + * use memory pool for speed). + * \code + class DefaultValueMapAllocator : public ValueMapAllocator + { + public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } + }; + * \endcode + */ +class JSON_API ValueMapAllocator +{ +public: + virtual ~ValueMapAllocator(); + virtual ValueInternalMap *newMap() = 0; + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; + virtual void destructMap( ValueInternalMap *map ) = 0; + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; + virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; + virtual ValueInternalLink *allocateMapLink() = 0; + virtual void releaseMapLink( ValueInternalLink *link ) = 0; +}; + +/** \brief ValueInternalMap hash-map bucket chain link (for internal use only). + * \internal previous_ & next_ allows for bidirectional traversal. + */ +class JSON_API ValueInternalLink +{ +public: + enum {itemPerLink = 6}; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. + enum InternalFlags { + flagAvailable = 0, + flagUsed = 1 + }; + + ValueInternalLink(); + + ~ValueInternalLink(); + + Value items_[itemPerLink]; + char *keys_[itemPerLink]; + ValueInternalLink *previous_; + ValueInternalLink *next_; +}; + +/** \brief A linked page based hash-table implementation used internally by Value. + * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked + * list in each bucket to handle collision. There is an addional twist in that + * each node of the collision linked list is a page containing a fixed amount of + * value. This provides a better compromise between memory usage and speed. + * + * Each bucket is made up of a chained list of ValueInternalLink. The last + * link of a given bucket can be found in the 'previous_' field of the following bucket. + * The last link of the last bucket is stored in tailLink_ as it has no following bucket. + * Only the last link of a bucket may contains 'available' item. The last link always + * contains at least one element unless is it the bucket one very first link. + */ +class JSON_API ValueInternalMap +{ + friend class ValueIteratorBase; + friend class Value; +public: + typedef unsigned int HashKey; + typedef unsigned int BucketIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState + { + IteratorState() + : map_(0) + , link_(0) + , itemIndex_(0) + , bucketIndex_(0) + { + } + ValueInternalMap *map_; + ValueInternalLink *link_; + BucketIndex itemIndex_; + BucketIndex bucketIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + ValueInternalMap(); + ValueInternalMap( const ValueInternalMap &other ); + ValueInternalMap &operator =( const ValueInternalMap &other ); + ~ValueInternalMap(); + + void swap( ValueInternalMap &other ); + + BucketIndex size() const; + + void clear(); + + bool reserveDelta( BucketIndex growth ); + + bool reserve( BucketIndex newItemCount ); + + const Value *find( const char *key ) const; + + Value *find( const char *key ); + + Value &resolveReference( const char *key, + bool isStatic ); + + void remove( const char *key ); + + void doActualRemove( ValueInternalLink *link, + BucketIndex index, + BucketIndex bucketIndex ); + + ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); + + Value &setNewItem( const char *key, + bool isStatic, + ValueInternalLink *link, + BucketIndex index ); + + Value &unsafeAdd( const char *key, + bool isStatic, + HashKey hashedKey ); + + HashKey hash( const char *key ) const; + + int compare( const ValueInternalMap &other ) const; + +private: + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void incrementBucket( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static const char *key( const IteratorState &iterator ); + static const char *key( const IteratorState &iterator, bool &isStatic ); + static Value &value( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + +private: + ValueInternalLink *buckets_; + ValueInternalLink *tailLink_; + BucketIndex bucketsSize_; + BucketIndex itemCount_; +}; + +/** \brief A simplified deque implementation used internally by Value. + * \internal + * It is based on a list of fixed "page", each page contains a fixed number of items. + * Instead of using a linked-list, a array of pointer is used for fast item look-up. + * Look-up for an element is as follow: + * - compute page index: pageIndex = itemIndex / itemsPerPage + * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] + * + * Insertion is amortized constant time (only the array containing the index of pointers + * need to be reallocated when items are appended). + */ +class JSON_API ValueInternalArray +{ + friend class Value; + friend class ValueIteratorBase; +public: + enum {itemsPerPage = 8}; // should be a power of 2 for fast divide and modulo. + typedef Value::ArrayIndex ArrayIndex; + typedef unsigned int PageIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState // Must be a POD + { + IteratorState() + : array_(0) + , currentPageIndex_(0) + , currentItemIndex_(0) + { + } + ValueInternalArray *array_; + Value **currentPageIndex_; + unsigned int currentItemIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + ValueInternalArray(); + ValueInternalArray( const ValueInternalArray &other ); + ValueInternalArray &operator =( const ValueInternalArray &other ); + ~ValueInternalArray(); + void swap( ValueInternalArray &other ); + + void clear(); + void resize( ArrayIndex newSize ); + + Value &resolveReference( ArrayIndex index ); + + Value *find( ArrayIndex index ) const; + + ArrayIndex size() const; + + int compare( const ValueInternalArray &other ) const; + +private: + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static Value &dereference( const IteratorState &iterator ); + static Value &unsafeDereference( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + static ArrayIndex indexOf( const IteratorState &iterator ); + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + void makeIterator( IteratorState &it, ArrayIndex index ) const; + + void makeIndexValid( ArrayIndex index ); + + Value **pages_; + ArrayIndex size_; + PageIndex pageCount_; +}; + +/** \brief Experimental: do not use. Allocator to customize Value internal array. + * Below is an example of a simple implementation (actual implementation use + * memory pool). + \code + class DefaultValueArrayAllocator : public ValueArrayAllocator + { + public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + return new ValueInternalArray(); + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + return new ValueInternalArray( other ); + } + + virtual void destruct( ValueInternalArray *array ) + { + delete array; + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + free( value ); + } + }; + \endcode + */ +class JSON_API ValueArrayAllocator +{ +public: + virtual ~ValueArrayAllocator(); + virtual ValueInternalArray *newArray() = 0; + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; + virtual void destructArray( ValueInternalArray *array ) = 0; + /** \brief Reallocate array page index. + * Reallocates an array of pointer on each page. + * \param indexes [input] pointer on the current index. May be \c NULL. + * [output] pointer on the new index of at least + * \a minNewIndexCount pages. + * \param indexCount [input] current number of pages in the index. + * [output] number of page the reallocated index can handle. + * \b MUST be >= \a minNewIndexCount. + * \param minNewIndexCount Minimum number of page the new index must be able to + * handle. + */ + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) = 0; + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) = 0; + virtual Value *allocateArrayPage() = 0; + virtual void releaseArrayPage( Value *value ) = 0; +}; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +/** \brief base class for Value iterators. + * + */ +class ValueIteratorBase { +public: + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + ValueIteratorBase(); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIteratorBase(const Value::ObjectValues::iterator ¤t); +#else + ValueIteratorBase( const ValueInternalArray::IteratorState &state ); + ValueIteratorBase( const ValueInternalMap::IteratorState &state ); +#endif + + bool operator ==(const SelfType &other) const { + return isEqual(other); + } + + bool operator !=(const SelfType &other) const { + return !isEqual(other); + } + + difference_type operator -(const SelfType &other) const { + return computeDistance(other); + } + + /// Return either the index or the member name of the referenced value as a Value. + Value key() const; + + /// Return the index of the referenced Value. -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value. "" if it is not an objectValue. + const char *memberName() const; + +protected: + Value &deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType &other) const; + + bool isEqual(const SelfType &other) const; + + void copy(const SelfType &other); + +private: +#ifndef JSON_VALUE_USE_INTERNAL_MAP + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; +#else + union + { + ValueInternalArray::IteratorState array_; + ValueInternalMap::IteratorState map_; + }iterator_; + bool isArray_; +#endif +}; + +/** \brief const iterator for object and array value. + * + */ +class ValueConstIterator: public ValueIteratorBase { + friend class Value; +public: + typedef unsigned int size_t; + typedef int difference_type; + typedef const Value &reference; + typedef const Value *pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); +private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueConstIterator(const Value::ObjectValues::iterator ¤t); +#else + ValueConstIterator( const ValueInternalArray::IteratorState &state ); + ValueConstIterator( const ValueInternalMap::IteratorState &state ); +#endif +public: + SelfType &operator =(const ValueIteratorBase &other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType &operator--() { + decrement(); + return *this; + } + + SelfType &operator++() { + increment(); + return *this; + } + + reference operator *() const { + return deref(); + } +}; + +/** \brief Iterator for object and array value. + */ +class ValueIterator: public ValueIteratorBase { + friend class Value; +public: + typedef unsigned int size_t; + typedef int difference_type; + typedef Value &reference; + typedef Value *pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator(const ValueConstIterator &other); + ValueIterator(const ValueIterator &other); +private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIterator(const Value::ObjectValues::iterator ¤t); +#else + ValueIterator( const ValueInternalArray::IteratorState &state ); + ValueIterator( const ValueInternalMap::IteratorState &state ); +#endif +public: + + SelfType &operator =(const SelfType &other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType &operator--() { + decrement(); + return *this; + } + + SelfType &operator++() { + increment(); + return *this; + } + + reference operator *() const { + return deref(); + } +}; + +} // namespace Json + +#endif // CPPTL_JSON_H_INCLUDED diff --git a/include/json/writer.h b/include/json/writer.h new file mode 100644 index 0000000..38f18cb --- /dev/null +++ b/include/json/writer.h @@ -0,0 +1,173 @@ +#ifndef JSON_WRITER_H_INCLUDED +# define JSON_WRITER_H_INCLUDED + +# include "value.h" +# include +# include +# include + +namespace Json { + +class Value; + +/** \brief Abstract class for writers. + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual std::string write(const Value &root) = 0; +}; + +/** \brief Outputs a Value in JSON format without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + */ +class JSON_API FastWriter: public Writer { +public: + FastWriter(); + virtual ~FastWriter() { + } + + void enableYAMLCompatibility(); + +public: + // overridden from Writer + virtual std::string write(const Value &root); + +private: + void writeValue(const Value &value); + + std::string document_; + bool yamlCompatiblityEnabled_; +}; + +/** \brief Writes a Value in JSON format in a human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + */ +class JSON_API StyledWriter: public Writer { +public: + StyledWriter(); + virtual ~StyledWriter() { + } + +public: + // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write(const Value &root); + +private: + void writeValue(const Value &value); + void writeArrayValue(const Value &value); + bool isMultineArray(const Value &value); + void pushValue(const std::string &value); + void writeIndent(); + void writeWithIndent(const std::string &value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value &root); + void writeCommentAfterValueOnSameLine(const Value &root); + bool hasCommentForValue(const Value &value); + static std::string normalizeEOL(const std::string &text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; +}; + +/** \brief Writes a Value in JSON format in a human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + */ +class JSON_API StyledStreamWriter { +public: + StyledStreamWriter(std::string indentation = "\t"); + ~StyledStreamWriter() { + } + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not return a value. + */ + void write(std::ostream &out, const Value &root); + +private: + void writeValue(const Value &value); + void writeArrayValue(const Value &value); + bool isMultineArray(const Value &value); + void pushValue(const std::string &value); + void writeIndent(); + void writeWithIndent(const std::string &value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value &root); + void writeCommentAfterValueOnSameLine(const Value &root); + bool hasCommentForValue(const Value &value); + static std::string normalizeEOL(const std::string &text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_; +}; + +std::string JSON_API valueToString(Int value); +std::string JSON_API valueToString(UInt value); +std::string JSON_API valueToString(double value); +std::string JSON_API valueToString(bool value); +std::string JSON_API valueToQuotedString(const char *value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +std::ostream& operator<<(std::ostream&, const Value &root); + +} // namespace Json + +#endif // JSON_WRITER_H_INCLUDED diff --git a/include/testcase.h b/include/testcase.h new file mode 100644 index 0000000..a9b79fd --- /dev/null +++ b/include/testcase.h @@ -0,0 +1,38 @@ +#include +#include + +#include +#include + +#include +using namespace std; + +#include + +class TestCase { +public: + TestCase(); + virtual ~TestCase(); + +public: + string result; // "pass" or "fail", "block", "N/A" + string start_at; + string end_at; + string std_out; + bool is_executed; + + // below m_case are sent from Com-module for each case. + Json::Value m_case; + string purpose; + string case_id; + int timeout_value; + + char m_str_time[32]; // to store the time string +public: + void init(const Json::Value value); // the case_node should be a string in json format + Json::Value to_json(); + Json::Value result_to_json(); + void set_result(string test_result, string test_msg); + void set_start_at(); + void getCurrentTime(); +}; diff --git a/makefile b/makefile new file mode 100644 index 0000000..08d5a3c --- /dev/null +++ b/makefile @@ -0,0 +1,75 @@ +TARGET = httpserver +UT_TARGET = ut +OBJ_PATH = objs +PREFIX_BIN = + +CC = g++ +INCLUDES = -I include +LIBS = +CFLAGS =-Wall -Werror +LINKFLAGS = -lpthread + +JSON_SRCDIR = src/json +SERVER_SRCDIR = src +MAIN_SRCDIR = src/main +UT_SRCDIR = src/ut + +JSON_SOURCES = $(foreach d,$(JSON_SRCDIR),$(wildcard $(d)/*.cpp) ) +JSON_OBJS = $(patsubst %.cpp, $(OBJ_PATH)/%.o, $(JSON_SOURCES)) + +SERVER_SOURCES = $(foreach d,$(SERVER_SRCDIR),$(wildcard $(d)/*.cpp) ) +SERVER_OBJS = $(patsubst %.cpp, $(OBJ_PATH)/%.o, $(SERVER_SOURCES)) + +MAIN_SOURCES = $(foreach d,$(MAIN_SRCDIR),$(wildcard $(d)/*.cpp) ) +MAIN_OBJS = $(patsubst %.cpp, $(OBJ_PATH)/%.o, $(MAIN_SOURCES)) + +UT_SOURCES = $(foreach d,$(UT_SRCDIR),$(wildcard $(d)/*.cpp) ) +UT_OBJS = $(patsubst %.cpp, $(OBJ_PATH)/%.o, $(UT_SOURCES)) + +default:init compile +ut:init ut_compile +ut: CC += -DDEBUG -g +debug: CC += -DDEBUG -g +debug: default + +$(C_OBJS):$(OBJ_PATH)/%.o:%.c + $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ + +$(JSON_OBJS):$(OBJ_PATH)/%.o:%.cpp + $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ + +$(SERVER_OBJS):$(OBJ_PATH)/%.o:%.cpp + $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ + +$(MAIN_OBJS):$(OBJ_PATH)/%.o:%.cpp + $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ + +$(UT_OBJS):$(OBJ_PATH)/%.o:%.cpp + $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ + +init: + $(foreach d,$(JSON_SRCDIR), mkdir -p $(OBJ_PATH)/$(d);) + $(foreach d,$(SERVER_SRCDIR), mkdir -p $(OBJ_PATH)/$(d);) + $(foreach d,$(MAIN_SRCDIR), mkdir -p $(OBJ_PATH)/$(d);) + $(foreach d,$(UT_SRCDIR), mkdir -p $(OBJ_PATH)/$(d);) + +ut_compile:$(JSON_OBJS) $(SERVER_OBJS) $(UT_OBJS) + $(CC) $^ -o $(UT_TARGET) $(LINKFLAGS) $(LIBS) + +compile:$(JSON_OBJS) $(SERVER_OBJS) $(MAIN_OBJS) + $(CC) $^ -o $(TARGET) $(LINKFLAGS) $(LIBS) + +clean: + rm -rf $(OBJ_PATH) + rm -f $(TARGET) + rm -f $(UT_TARGET) + +install: $(TARGET) + #cp $(TARGET) $(PREFIX_BIN) + install -d $(DESTDIR)/usr/bin/ + install -m 755 $(TARGET) $(DESTDIR)/usr/bin/ + +uninstall: + rm -f $(PREFIX_BIN)/$(TARGET) + +rebuild: clean compile diff --git a/packaging/testkit-stub.debug.spec b/packaging/testkit-stub.debug.spec new file mode 100644 index 0000000..fa49a6f --- /dev/null +++ b/packaging/testkit-stub.debug.spec @@ -0,0 +1,57 @@ + +Name: testkit-stub +Summary: test +Version: 1.0 +Release: 1 +Group: Development/Debug +License: GPL v2 only +URL: http://www.moblin.org/ +Source0: %{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description +test + + +%prep +%setup -q -n %{name}-%{version} +# >> setup +# << setup + +%build +# >> build pre +# << build pre + + +# Call make instruction with smp support +make debug %{?jobs:-j%jobs} + +# >> build post +# << build post +%install +rm -rf %{buildroot} +# >> install pre +# << install pre +%make_install + +# >> install post +# << install post + +%clean +rm -rf %{buildroot} + + + + + + +%files +%defattr(-,root,root,-) +# >> files +%{_bindir}/httpserver +# << files + + +%changelog +* Tue Mar 21 2013 Li Min 0.10 +- create for SLP build diff --git a/packaging/testkit-stub.spec b/packaging/testkit-stub.spec new file mode 100644 index 0000000..6569f72 --- /dev/null +++ b/packaging/testkit-stub.spec @@ -0,0 +1,57 @@ + +Name: testkit-stub +Summary: test +Version: 1.0 +Release: 1 +Group: Development/Debug +License: GPL v2 only +URL: http://www.moblin.org/ +Source0: %{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description +test + + +%prep +%setup -q -n %{name}-%{version} +# >> setup +# << setup + +%build +# >> build pre +# << build pre + + +# Call make instruction with smp support +make %{?jobs:-j%jobs} + +# >> build post +# << build post +%install +rm -rf %{buildroot} +# >> install pre +# << install pre +%make_install + +# >> install post +# << install post + +%clean +rm -rf %{buildroot} + + + + + + +%files +%defattr(-,root,root,-) +# >> files +%{_bindir}/httpserver +# << files + + +%changelog +* Tue Mar 21 2013 Li Min 0.10 +- create for SLP build diff --git a/src/comfun.cpp b/src/comfun.cpp new file mode 100644 index 0000000..8676629 --- /dev/null +++ b/src/comfun.cpp @@ -0,0 +1,78 @@ +#include +#include "comfun.h" + +ComFun::ComFun() { + +} +ComFun::~ComFun() { + +} + +char ComFun::CharToInt(char ch) { + if (ch >= '0' && ch <= '9') + return (char) (ch - '0'); + if (ch >= 'a' && ch <= 'f') + return (char) (ch - 'a' + 10); + if (ch >= 'A' && ch <= 'F') + return (char) (ch - 'A' + 10); + return -1; +} + +char ComFun::StrToBin(char *str) { + char tempWord[2]; + char chn; + tempWord[0] = CharToInt(str[0]); //make the B to 11 -- 00001011 + tempWord[1] = CharToInt(str[1]); //make the 0 to 0 -- 00000000 + chn = (tempWord[0] << 4) | tempWord[1]; //to change the BO to 10110000 + return chn; +} +//url decode +char* ComFun::UrlDecode(const char *str) { + char tmp[2]; + int i = 0, len = strlen(str); + char *output = new char[len + 1]; + memset(output, 0, len + 1); + int j = 0; + while (i < len) { + if (*(str + i) == '%') { + tmp[0] = *(str + i + 1); + tmp[1] = *(str + i + 2); + *(output + j) = StrToBin(tmp); + i = i + 3; + j++; + } else if (*(str + i) == '+') { + *(output + j) = ' '; + i++; + j++; + } else { + *(output + j) = *(str + i); + i++; + j++; + } + } + return output; +} + +//split function for string +std::vector ComFun::split(std::string str, std::string pattern) { + std::string::size_type pos; + std::vector < std::string > result; + str += pattern; //extend string so that operate easyly + unsigned int size = str.size(); + int count = 0; + for (unsigned int i = 0; i < size; i++) { + if ((pattern == "=")&&(count == 1)){ + std::string s = str.substr(i, size-1-i); + result.push_back(s); + break; + } + pos = str.find(pattern, i); + if (pos < size) { + std::string s = str.substr(i, pos - i); + result.push_back(s); + i = pos + pattern.size() - 1; + } + count++; + } + return result; +} \ No newline at end of file diff --git a/src/httpserver.cpp b/src/httpserver.cpp new file mode 100644 index 0000000..b9ff5a3 --- /dev/null +++ b/src/httpserver.cpp @@ -0,0 +1,879 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "comfun.h" +#include "httpserver.h" + +// if use 8080 for SERVER_PORT, it will not work with chrome +#define SERVER_PORT 8000 +#define MAX_BUF 102400 + +#define GET 0 +#define HEAD 2 +#define POST 3 +#define BAD_REQUEST -1 + +#if defined(DEBUG) | defined(_DEBUG) + #ifndef DBG_ONLY + #define DBG_ONLY(x) do { x } while (0) + #endif +#else + #ifndef DBG_ONLY + #define DBG_ONLY(x) + #endif +#endif + +HttpServer::HttpServer() { + m_test_cases = NULL; + m_exeType = "auto"; // set default to auto + m_totalBlocks = 0; + m_current_block_index = 1; + m_totalcaseCount = 0; + m_block_case_index = 0; + m_block_case_count = 0; + m_total_case_index = 0; + m_last_auto_result = "N/A"; + m_running_session = ""; + m_server_checked = false; + g_show_log = false; + g_port = ""; + g_hide_status = ""; + g_test_suite = ""; + g_exe_sequence = ""; + g_run_wiget = false; + g_enable_memory_collection = ""; + m_block_finished = false; + m_set_finished = false; + m_timeout_count = 0; + m_failto_launch = 0; + m_killing_widget = false; + m_rerun = false; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = timer_handler; + sigaction(SIGALRM, &sa, NULL); +} + +HttpServer::~HttpServer() { + if (m_test_cases) { + delete[] m_test_cases; + m_test_cases = NULL; + } + DBG_ONLY(outputFile.close();); +} + +// generate response code. send response +void HttpServer::sendresponse(int s, int code, struct HttpRequest *prequest, + string content) { + string buffer; + stringstream len_stream; + len_stream << content.length(); + prequest->responsecode = code; + // generate response head + switch (code) { + case 200: { + buffer = + "HTTP/1.1 200 OK\r\nServer: testkit-stub/1.0\r\nContent-Type: " + + prequest->prefix + + "\r\nAccept-Ranges: bytes\r\nContent-Length: " + + len_stream.str() + + "\r\nConnection: close\r\nAccess-Control-Allow-Origin: *\r\n\r\n" + + content; + break; + } + default: + break; + } + + if (g_show_log) cout << buffer << endl; + // send out the http response + send(s, buffer.c_str(), buffer.length(), 0); +} + +// parse the http request +int HttpServer::getrequest(string requestbuf, struct HttpRequest *prequest) { + std::vector < std::string > splitstr = ComFun::split(requestbuf, " "); + if (splitstr.size() >= 2) { + prequest->method = splitstr[0]; + prequest->path = splitstr[1]; + } + + if (prequest->path.find('?') == string::npos) { + //get the com module send data + int content_index = requestbuf.find("\r\n\r\n", 0); + if (content_index > -1) { + prequest->content = requestbuf.substr( + content_index + strlen("\r\n\r\n")); + } + } else { + int session_index = prequest->path.find("?"); + prequest->content = prequest->path.substr(session_index + 1); + } + if (prequest->method == "GET") { + return GET; + } else if (prequest->method == "POST") { + return POST; + } + return -1; +} + +// parse the test case data sent by com-module with init_test cmd +void HttpServer::parse_json_str(string case_node) { + Json::Reader reader; + Json::Value value; + + bool parsed = reader.parse(case_node, value); + if (!parsed) // try to parse as a file if parse fail + { // "test.json" is for verify + cout << case_node << endl; + std::ifstream test(case_node.c_str(), std::ifstream::binary); + parsed = reader.parse(test, value, false); + } + + if (parsed) { + m_totalBlocks = atoi(value["totalBlk"].asString().c_str()); + m_current_block_index = atoi(value["currentBlk"].asString().c_str()); + m_totalcaseCount = atoi(value["casecount"].asString().c_str()); + m_exeType = value["exetype"].asString(); + + const Json::Value arrayObj = value["cases"]; + m_block_case_count = arrayObj.size(); + + if (m_test_cases) { + delete[] m_test_cases; + m_test_cases = NULL; + } + m_test_cases = new TestCase[m_block_case_count]; + + for (int i = 0; i < m_block_case_count; i++) { + m_test_cases[i].init(arrayObj[i]); + } + } +} + +void HttpServer::cancel_time_check() { + timer.it_value.tv_sec = 0; + timer.it_value.tv_usec = 0; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 0; + setitimer(ITIMER_REAL, &timer, NULL); // set timer with value 0 will stop the timer + // refer to http://linux.die.net/man/2/setitimer, each process only have 1 timer of the same type. +} + +// set timeout value to 90 seconds for each case +void HttpServer::set_timer(int timeout_value) { + timer.it_value.tv_sec = timeout_value; + timer.it_value.tv_usec = 0; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 0; + int ret = setitimer(ITIMER_REAL, &timer, NULL); + if (ret < 0) + perror("error: set timer!!!"); +} + + +/** send out response according to different http request. +a typical workflow of auto case would be +/check_server_status (by com-module) +/init_test (by com-module) +/check_server (by widget) +/init_session_id?session_id=2033 (by widget) +/auto_test_task?session_id=2033 (by widget) +/check_execution_progress?session_id=2033 (by widget) +/ask_next_step?session_id=2033 (by widget) +/commit_result (by widget) +/auto_test_task?session_id=2033 (by widget) +/manual_cases (by widget) +/check_server_status (by com-module) +/get_test_result (by com-module) +/shut_down_server (by com-module) + + +a typical workflow of manual case would be +/check_server_status (by com-module) +/init_test (by com-module) +/check_server (by widget) +/init_session_id?session_id=2033 (by widget) +/auto_test_task?session_id=2033 (by widget) +/manual_cases (by widget) +/check_server_status (by com-module) +/commit_manual_result (by widget) +... +/check_server_status (by com-module) +/commit_manual_result (by widget) +/generate_xml (by widget) +/get_test_result (by com-module) +/shut_down_server (by com-module) + +**/ +void HttpServer::processpost(int s, struct HttpRequest *prequest) { + prequest->prefix = "application/json"; + string json_str = ""; + string json_parse_str = ""; + + if (prequest->path.find("/init_test") != string::npos) {// invoke by com-module to send test data + m_block_finished = false; + m_set_finished = false; + m_timeout_count = 0; + m_server_checked = false; + cout << "[ init the test suite ]" << endl; + DBG_ONLY(outputFile << "[ init the test suite ]" << endl;); + parse_json_str(prequest->content); + + start_client(); + //set_timer(30); // set timer here incase widget hang. + + m_block_case_index = 0; + if (m_current_block_index == 1) + m_total_case_index = 0; + + json_str = "{\"OK\":1}"; + } else if (prequest->path == "/check_server") {// invoke by index.html to find server running or not + cout << "[ checking server, and found the server is running ]" << endl; + DBG_ONLY(outputFile << "[ checking server, and found the server is running ]" << endl;); + m_server_checked = true; + json_str = "{\"OK\":1}"; + } else if (prequest->path == "/check_server_status") {// invoke by com-module to get server status + Json::Value status; + status["block_finished"] = m_block_finished ? 1 : 0; + status["finished"] = m_set_finished ? 1 : 0; + if (m_failto_launch > 10) { + status["error_code"] = 2; + status["finished"] = 1; // finish current set if can't launch widget + } + json_str = status.toStyledString(); + + if (!m_server_checked) { + cout << "waiting for widget check_server" << endl; + DBG_ONLY(outputFile << "wait for widget check_server" << endl;); + } + if (m_totalBlocks > 0) + { + cout << "block: " << m_current_block_index << "/" << m_totalBlocks << ", total case: " << m_total_case_index << "/" << m_totalcaseCount << ", block case: " << m_block_case_index << "/" << m_block_case_count << ", m_timeout_count:" << m_timeout_count << endl; + DBG_ONLY(outputFile << "block: " << m_current_block_index << "/" << m_totalBlocks << ", total case: " << m_total_case_index << "/" << m_totalcaseCount << ", block case: " << m_block_case_index << "/" << m_block_case_count << ", m_timeout_count:" << m_timeout_count << endl;); + if (m_exeType != "auto") { + cout << "manual cases. please check device." << endl << endl; + DBG_ONLY(outputFile << "manual cases. please click on device." << endl << endl;); + } + } + + } else if (prequest->path == "/shut_down_server") { + if (g_run_wiget == true) + killAllWidget(); // kill all widget when shutdown server + json_str = "{\"OK\":1}"; + gIsRun = 0; + } else if (prequest->path.find("/init_session_id") != string::npos) {// invoke by index.html to record a session id + json_str = "{\"OK\":1}"; + int index = prequest->path.find('='); + if (index != -1) { + m_running_session = prequest->path.substr(index + 1); + cout << "[ sessionID: " << m_running_session << " is gotten from the client ]" << endl; + DBG_ONLY(outputFile << "[ sessionID: " << m_running_session << " is gotten from the client ]" << endl;); + } else { + cout << "[ invalid session id ]" << endl; + DBG_ONLY(outputFile << "[ invalid session id ]" << endl;); + } + } else if (prequest->path.find("/ask_next_step") != string::npos) {// invoke by index.html to check whether there are more cases + if (m_block_finished || m_set_finished) + json_str = "{\"step\":\"stop\"}"; + else + json_str = "{\"step\":\"continue\"}"; + + m_timeout_count = 0; // reset the timeout count + } else if (prequest->path.find("/auto_test_task") != string::npos) {// invoke by index.html to get current auto case + if (m_test_cases == NULL) { + json_str = "{\"Error\":\"no case\"}"; + } else if (m_exeType != "auto") { + json_str = "{\"none\":0}"; + } else { + string error_type = ""; + bool find_tc = get_auto_case(prequest->content, &error_type); + if (find_tc == false) { + json_str = "{\"" + error_type + "\":0}"; + } else { + json_str = + m_test_cases[m_block_case_index].to_json().toStyledString(); + } + } + } else if (prequest->path.find("/manual_cases") != string::npos) {// invoke by index.html to get all manual cases + cancel_time_check(); // should not timeout in manual mode + if (!m_test_cases) { + json_str = "{\"Error\":\"no case\"}"; + } else if (m_exeType == "auto") { + json_str = "{\"none\":0}"; + } else { + Json::Value arrayObj; + for (int i = 0; i < m_block_case_count; i++) + arrayObj.append(m_test_cases[i].to_json()); + + json_str = arrayObj.toStyledString(); + } + } else if (prequest->path.find("/case_time_out") != string::npos) {// invoke by timer to notify case timeout + if (!m_test_cases) { + json_str = "{\"Error\":\"no case\"}"; + } else if (m_block_case_index < m_block_case_count) { + checkResult(&m_test_cases[m_block_case_index]); + json_str = "{\"OK\":\"timeout\"}"; + } else { + json_str = "{\"Error\":\"case out of index\"}"; + } + } else if (prequest->path.find("/commit_manual_result") != string::npos) {// invoke by index.html to provide result of a manual case. + if ((prequest->content.length() == 0) || (!m_test_cases)) { + json_str = "{\"Error\":\"no manual result\"}"; + } else { + find_purpose(prequest, false); // will set index in find_purpose + json_str = "{\"OK\":1}"; + } + } else if (prequest->path.find("/check_execution_progress") != string::npos) {//invoke by index.html to get test result of last auto case + char *total_count = new char[16]; + sprintf(total_count, "%d", m_totalcaseCount); + char *current_index = new char[16]; + sprintf(current_index, "%d", m_total_case_index + 1); + + string count_str(total_count); + string index_str(current_index); + json_str = "{\"total\":" + count_str + ",\"current\":" + index_str + + ",\"last_test_result\":\"" + m_last_auto_result + "\"}"; + + delete[] total_count; + delete[] current_index; + } + //generate_xml:from index_html, a maually block finished when click done in widget + else if (prequest->path == "/generate_xml") { + cancel_time_check(); + m_block_finished = true; + if (m_current_block_index == m_totalBlocks) + m_set_finished = true; + + json_str = "{\"OK\":1}"; + } + //from com module,when m_set_finished is true + else if (prequest->path == "/get_test_result") { + cancel_time_check(); + if (!m_test_cases) { + json_str = "{\"Error\":\"no case\"}"; + } else { + Json::Value root; + Json::Value arrayObj; + for (int i = 0; i < m_block_case_count; i++) + arrayObj.append(m_test_cases[i].result_to_json()); + + char count[8]; + memset(count, 0, 8); + sprintf(count, "%d", m_block_case_count); + root["count"] = count; + root["cases"] = arrayObj; + + json_str = root.toStyledString(); + } + } + // index.html invoke this with purpose and result of an auto case, auto case commit result. + // we need find correct test case by purpose, and record test result to it. + else if (prequest->path == "/commit_result") { + if ((prequest->content.length() == 0) || (!m_test_cases)) { + json_str = "{\"Error\":\"no result\"}"; + m_last_auto_result = "BLOCK"; + m_rerun = true; + } else { + char* tmp = ComFun::UrlDecode(prequest->content.c_str()); + string content = tmp; + delete[] tmp; // free memory from comfun + string sessionid = ""; + std::vector < std::string > splitstr = ComFun::split(content, "&"); + for (unsigned int i = 0; i < splitstr.size(); i++) { + vector < string > resultkey = ComFun::split(splitstr[i], "="); + if (resultkey[0] == "session_id"){ + sessionid = resultkey[1]; + break; + } + } + if (m_running_session == sessionid) { + m_rerun = false; + if (m_block_case_index < m_block_case_count) { + m_block_case_index++; + m_total_case_index++; + } + + find_purpose(prequest, true); + + json_str = "{\"OK\":1}"; + } + } + } else if (prequest->path == "/set_capability") {// by com-module to send capability data + Json::Reader reader; + + reader.parse(prequest->content, m_capability); + + json_str = "{\"OK\":1}"; + } else if (prequest->path.find("/capability") != string::npos) {// by test suite. only one query parameter each time + char* tmp = ComFun::UrlDecode(prequest->content.c_str()); + string content = tmp; + delete[] tmp; // free memory from comfun + + json_str = "{\"support\":0}"; + string name = "", value = ""; + std::vector < std::string > splitstr = ComFun::split(content, "&"); + for (unsigned int i = 0; i < splitstr.size(); i++) { + vector < string > resultkey = ComFun::split(splitstr[i], "="); + if (resultkey[0] == "name") { + name = resultkey[1]; + for (unsigned int i = 0; i < name.size(); i++) + name[i] = tolower(name[i]); + } + if (resultkey[0] == "value") value = resultkey[1]; + } + if (m_capability[name].isBool()) {// for bool value, omit the value part + json_str = "{\"support\":1}"; + } + else if (m_capability[name].isInt()) { + if (m_capability[name].asInt() == atoi(value.c_str())) + json_str = "{\"support\":1}"; + } + else if (m_capability[name].isString()) { + if (m_capability[name].asString() == value) + json_str = "{\"support\":1}"; + } + } else { + cout << "=================unknown request: " << prequest->path << endl; + } + + DBG_ONLY( + outputFile << "prequest->path is:" << prequest->path << endl; + outputFile << "prequest->content is:" << prequest->content << endl; + outputFile << "server response is:" << json_str << endl; + ); + if (g_show_log == true) + { + cout << "prequest->path is:" << prequest->path << endl; + cout << "prequest->content is:" << prequest->content << endl; + cout << "server response is:" << json_str << endl; + } + + if (json_str != "") + sendresponse(s, 200, prequest, json_str); +} + +// find correct case according the purpose sent by widget +void HttpServer::find_purpose(struct HttpRequest *prequest, bool auto_test) { + string purpose = ""; + string result = ""; + string msg = ""; + string content = ""; + + char* tmp = ComFun::UrlDecode(prequest->content.c_str()); + content = tmp; + delete[] tmp; // free memory from comfun + + std::vector < std::string > splitstr = ComFun::split(content, "&"); + for (unsigned int i = 0; i < splitstr.size(); i++) { + vector < string > resultkey = ComFun::split(splitstr[i], "="); + if (resultkey[0] == "purpose") + purpose = resultkey[1]; + else if (resultkey[0] == "result") + result = resultkey[1]; + else if (resultkey[0] == "msg") + msg = resultkey[1]; + } + + bool found = false; + for (int i = 0; i < m_block_case_count; i++) { + if (m_test_cases[i].purpose == purpose) { + m_test_cases[i].set_result(result, msg); + found = true; + if (!auto_test) // should auto test use this logic? + m_block_case_index = i; // set index by purpose found + break; + } + } + if (!found) { + cout << "[ Error: can't find any test case by key: " << purpose << " ]" << endl; + DBG_ONLY(outputFile << "[ Error: can't find any test case by key: " << purpose << " ]" << endl;); + } + + if (auto_test) + m_last_auto_result = result; +} + +// create new thread for each http request +void* processthread(void *para) { + string recvstr = ""; + char *buffer = new char[MAX_BUF]; // suppose 1 case need 1k, 100 cases will be sent each time, we need 100k memory? + memset(buffer, 0, MAX_BUF); + long iDataNum = 0; + int recvnum = 0; + HttpServer *server = (HttpServer *) para; + struct HttpRequest httprequest; + httprequest.content = ""; + httprequest.path = ""; + + int content_length = 0; + int real_content_length = 0; + while (1) { + iDataNum = recv(server->clientsocket, buffer + recvnum, + MAX_BUF - recvnum - 1, 0); + if (iDataNum <= 0) { + delete[] buffer; + close(server->clientsocket); + pthread_exit (NULL); + return 0; + } + recvnum += iDataNum; + + // to calculate content length and real content read + if (content_length == 0) { + char* Content_Length = strstr(buffer, "Content-Length: "); + if (Content_Length != NULL) { + char tmplen[32]; + sscanf(Content_Length, "%[^:]:%d", tmplen, &content_length); + if (content_length > 0) { + char* tmp = strstr(buffer, "\r\n\r\n"); + if (tmp == NULL) { + tmp = strstr(buffer, "\n\n"); + if (tmp != NULL) + real_content_length = iDataNum - (tmp-buffer) -2; + else { + cout << "[ can't find \\r\\n\\r\\n or \\n\\n ]" << endl; + break; + } + } + else real_content_length = iDataNum - (tmp-buffer) - 4; + } + } + else real_content_length += iDataNum; + } + else real_content_length += iDataNum; + + if (real_content_length >= content_length) break; + } + buffer[recvnum] = '\0'; + recvstr = buffer; + delete[] buffer; + + // parse request and process it + switch (server->getrequest(recvstr, &httprequest)) { + case GET: + case POST: + server->processpost(server->clientsocket, &httprequest); + break; + default: + break; + } + close(server->clientsocket); + pthread_exit (NULL); + return 0; +} + +// send out timeout request to server if timeout. timer handler must be a global function, so can't invoke server's member function directly. +void timer_handler(int signum) { + // when timeout, we send a http request to server, so server can check result. seems no other convinent way to do it. + const char *strings = + "GET /case_time_out HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: Close\r\n\r\n"; + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr("127.0.0.1"); + address.sin_port = htons(SERVER_PORT); + int len = sizeof(address); + int result = connect(sockfd, (struct sockaddr *) &address, len); + if (result == -1) { + cout << "fail to send timeout request to server!" << endl; + } else { + write(sockfd, strings, strlen(strings)); + close(sockfd); + } +} + +void HttpServer::print_info_string(string case_id) { + if (m_rerun) { + cout << "\n[case] execute case again: "; + DBG_ONLY(outputFile << "\n[case] execute case again: ";); + } + else { + cout << "\n[case] execute case: "; + DBG_ONLY(outputFile << "\n[case] execute case: ";); + } + cout << case_id << endl; + cout << "last_test_result: " << m_last_auto_result << endl; + DBG_ONLY(outputFile << case_id << endl;); + DBG_ONLY(outputFile << "last_test_result: " << m_last_auto_result << endl;); +} + +// prepare to run current auto case by set the start time, etc. +bool HttpServer::get_auto_case(string content, string *type) { + if (!m_killing_widget) { + if (content != "") { + string value = content.substr(content.find("=") + 1); + if (value.length() > 0) { + if (m_running_session == value) { + if (m_block_case_index < m_block_case_count) { + set_timer(m_test_cases[m_block_case_index].timeout_value); + m_test_cases[m_block_case_index].set_start_at(); + print_info_string(m_test_cases[m_block_case_index].case_id); + return true; + } else { + cout << endl << "[ no auto case is available any more ]" << endl; + DBG_ONLY(outputFile << endl << "[ no auto case is available any more ]" << endl;); + *type = "none"; + m_block_finished = true; + if (m_current_block_index == m_totalBlocks) + m_set_finished = true; // the set is finished if current block is the last block + } + } else { + cout << "[ Error: invalid session ID ]" << endl; + DBG_ONLY(outputFile << "[ Error: invalid session ID ]" << endl;); + *type = "invalid"; + } + } + } + } else { + cout << "\n[ restart client process is activated, exit current client ]" << endl; + DBG_ONLY(outputFile << "\n[ restart client process is activated, exit current client ]" << endl;); + *type = "stop"; + } + return false; +} + +//start the socket server, listen to client +void HttpServer::StartUp() { + DBG_ONLY( + outputFile.open("httpserver_log.txt",ios::out); + outputFile<<"httpserver.g_port is:"+g_port<is_executed) { + cout << "[ Warning: time is out, test case \"" << testcase->purpose << "\" is timeout, set the result to \"BLOCK\", and restart the client ]" << endl; + DBG_ONLY(outputFile << "[ Warning: time is out, test case \"" << testcase->purpose << "\" is timeout, set the result to \"BLOCK\", and restart the client ]" << endl;); + + testcase->set_result("BLOCK", "Time is out"); + m_last_auto_result = "BLOCK"; + + if (g_run_wiget == true){ + cout << "[ start new client in 5sec ]" << endl; + DBG_ONLY(outputFile << "[ start new client in 5sec ]" << endl;); + sleep(5); + start_client(); // start widget again in case it dead. browser not need to restart + } + m_timeout_count++; + if (m_block_case_index < m_block_case_count) { + m_block_case_index++; + m_total_case_index++; + } + } else { + cout << "[ test case \"" << testcase->purpose << "\" is executed in time, and the result is testcase->result ]" << endl; + DBG_ONLY(outputFile << "[ test case \"" << testcase->purpose << "\" is executed in time, and the result is testcase->result ]" << endl;); + } + +} + +void HttpServer::getAllWidget() { + char buf[128]; + memset(buf, 0, 128); + FILE *pp; + string cmd = g_launcher+" -l | awk '{print $NF}' | sed -n '3,$p'"; + if ((pp = popen(cmd.c_str(), "r")) == NULL) { + cout << "popen() error!" << endl; + DBG_ONLY(outputFile << "popen() error!" << endl;); + return; + } + + while (fgets(buf, sizeof buf, pp)) { + buf[strlen(buf) - 1] = 0; // remove the character return at the end. + m_widgets.push_back(buf); + memset(buf, 0, 128); + } + pclose(pp); +} + +// try to kill all widget listed by wrt-launch -l +void HttpServer::killAllWidget() { + m_killing_widget = true; + string cmd = "killall " + g_launcher + " 2>/dev/null"; + system(cmd.c_str()); + + char buf[128]; + memset(buf, 0, 128); + FILE *pp; + if ((pp = popen("ps ax | awk '{print $NF}' | sed -n '2,$p'", "r")) == NULL) { + cout << "popen() error!" << endl; + DBG_ONLY(outputFile << "popen() error!" << endl;); + return; + } + + std::vector processes; + while (fgets(buf, sizeof buf, pp)) { + if(g_show_log == true) + cout< 10) { + m_set_finished = true; + m_block_finished = true; + m_total_case_index = m_totalcaseCount; + break; + } + run_cmd(g_kill_cmd, "result: killed", true); + sleep(10); // try until start widget success + } + m_failto_launch = 0; + } + else{ + sleep(5); // sleep 5 seconds to avoid launch browser too frequently, otherwise may launch fail. + string launch_cmd = ""; + launch_cmd = g_launcher +"&"; + int status = system(launch_cmd.c_str()); + if(status != 0) + { + cout<<"[cmd: "< +# include + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +namespace Json { + +/* Fast memory allocator. + * + * This memory allocator allocates memory for a batch of object (specified by + * the page size, the number of object in each page). + * + * It does not allow the destruction of a single object. All the allocated objects + * can be destroyed at once. The memory can be either released or reused for future + * allocation. + * + * The in-place new operator must be used to construct the object using the pointer + * returned by allocate. + */ +template +class BatchAllocator { +public: + typedef AllocatedType Type; + + BatchAllocator(unsigned int objectsPerPage = 255) : + freeHead_(0), objectsPerPage_(objectsPerPage) { +// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); + assert( + sizeof(AllocatedType) * objectPerAllocation + >= sizeof(AllocatedType *)); // We must be able to store a slist in the object free space. + assert(objectsPerPage >= 16); + batches_ = allocateBatch(0); // allocated a dummy page + currentBatch_ = batches_; + } + + ~BatchAllocator() { + for (BatchInfo *batch = batches_; batch;) { + BatchInfo *nextBatch = batch->next_; + free(batch); + batch = nextBatch; + } + } + + /// allocate space for an array of objectPerAllocation object. + /// @warning it is the responsability of the caller to call objects constructors. + AllocatedType *allocate() { + if (freeHead_) // returns node from free list. + { + AllocatedType *object = freeHead_; + freeHead_ = *(AllocatedType **) object; + return object; + } + if (currentBatch_->used_ == currentBatch_->end_) { + currentBatch_ = currentBatch_->next_; + while (currentBatch_ && currentBatch_->used_ == currentBatch_->end_) + currentBatch_ = currentBatch_->next_; + + if (!currentBatch_) // no free batch found, allocate a new one + { + currentBatch_ = allocateBatch(objectsPerPage_); + currentBatch_->next_ = batches_; // insert at the head of the list + batches_ = currentBatch_; + } + } + AllocatedType *allocated = currentBatch_->used_; + currentBatch_->used_ += objectPerAllocation; + return allocated; + } + + /// Release the object. + /// @warning it is the responsability of the caller to actually destruct the object. + void release(AllocatedType *object) { + assert(object != 0); + *(AllocatedType **) object = freeHead_; + freeHead_ = object; + } + +private: + struct BatchInfo { + BatchInfo *next_; + AllocatedType *used_; + AllocatedType *end_; + AllocatedType buffer_[objectPerAllocation]; + }; + + // disabled copy constructor and assignement operator. + BatchAllocator(const BatchAllocator &); + void operator =(const BatchAllocator &); + + static BatchInfo *allocateBatch(unsigned int objectsPerPage) { + const unsigned int mallocSize = sizeof(BatchInfo) + - sizeof(AllocatedType) * objectPerAllocation + + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; + BatchInfo *batch = static_cast(malloc(mallocSize)); + batch->next_ = 0; + batch->used_ = batch->buffer_; + batch->end_ = batch->buffer_ + objectsPerPage; + return batch; + } + + BatchInfo *batches_; + BatchInfo *currentBatch_; + /// Head of a single linked list within the allocated space of freeed object + AllocatedType *freeHead_; + unsigned int objectsPerPage_; +}; + +} // namespace Json + +# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION +#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED diff --git a/src/json/json_internalarray.inl b/src/json/json_internalarray.inl new file mode 100644 index 0000000..9b985d2 --- /dev/null +++ b/src/json/json_internalarray.inl @@ -0,0 +1,448 @@ +// included by json_value.cpp +// everything is within Json namespace + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueArrayAllocator::~ValueArrayAllocator() +{ +} + +// ////////////////////////////////////////////////////////////////// +// class DefaultValueArrayAllocator +// ////////////////////////////////////////////////////////////////// +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + return new ValueInternalArray(); + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + return new ValueInternalArray( other ); + } + + virtual void destructArray( ValueInternalArray *array ) + { + delete array; + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + free( value ); + } +}; + +#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + ValueInternalArray *array = arraysAllocator_.allocate(); + new (array) ValueInternalArray(); // placement new + return array; + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + ValueInternalArray *array = arraysAllocator_.allocate(); + new (array) ValueInternalArray( other ); // placement new + return array; + } + + virtual void destructArray( ValueInternalArray *array ) + { + if ( array ) + { + array->~ValueInternalArray(); + arraysAllocator_.release( array ); + } + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( pagesAllocator_.allocate() ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + pagesAllocator_.release( value ); + } +private: + BatchAllocator arraysAllocator_; + BatchAllocator pagesAllocator_; +}; +#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR + +static ValueArrayAllocator *&arrayAllocator() +{ + static DefaultValueArrayAllocator defaultAllocator; + static ValueArrayAllocator *arrayAllocator = &defaultAllocator; + return arrayAllocator; +} + +static struct DummyArrayAllocatorInitializer { + DummyArrayAllocatorInitializer() + { + arrayAllocator(); // ensure arrayAllocator() statics are initialized before main(). + } +} dummyArrayAllocatorInitializer; + +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +bool +ValueInternalArray::equals( const IteratorState &x, + const IteratorState &other ) +{ + return x.array_ == other.array_ + && x.currentItemIndex_ == other.currentItemIndex_ + && x.currentPageIndex_ == other.currentPageIndex_; +} + + +void +ValueInternalArray::increment( IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && + (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ + != it.array_->size_, + "ValueInternalArray::increment(): moving iterator beyond end" ); + ++(it.currentItemIndex_); + if ( it.currentItemIndex_ == itemsPerPage ) + { + it.currentItemIndex_ = 0; + ++(it.currentPageIndex_); + } +} + + +void +ValueInternalArray::decrement( IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_ + && it.currentItemIndex_ == 0, + "ValueInternalArray::decrement(): moving iterator beyond end" ); + if ( it.currentItemIndex_ == 0 ) + { + it.currentItemIndex_ = itemsPerPage-1; + --(it.currentPageIndex_); + } + else + { + --(it.currentItemIndex_); + } +} + + +Value & +ValueInternalArray::unsafeDereference( const IteratorState &it ) +{ + return (*(it.currentPageIndex_))[it.currentItemIndex_]; +} + + +Value & +ValueInternalArray::dereference( const IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && + (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ + < it.array_->size_, + "ValueInternalArray::dereference(): dereferencing invalid iterator" ); + return unsafeDereference( it ); +} + +void +ValueInternalArray::makeBeginIterator( IteratorState &it ) const +{ + it.array_ = const_cast( this ); + it.currentItemIndex_ = 0; + it.currentPageIndex_ = pages_; +} + + +void +ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const +{ + it.array_ = const_cast( this ); + it.currentItemIndex_ = index % itemsPerPage; + it.currentPageIndex_ = pages_ + index / itemsPerPage; +} + + +void +ValueInternalArray::makeEndIterator( IteratorState &it ) const +{ + makeIterator( it, size_ ); +} + + +ValueInternalArray::ValueInternalArray() + : pages_( 0 ) + , size_( 0 ) + , pageCount_( 0 ) +{ +} + + +ValueInternalArray::ValueInternalArray( const ValueInternalArray &other ) + : pages_( 0 ) + , pageCount_( 0 ) + , size_( other.size_ ) +{ + PageIndex minNewPages = other.size_ / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); + JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, + "ValueInternalArray::reserve(): bad reallocation" ); + IteratorState itOther; + other.makeBeginIterator( itOther ); + Value *value; + for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) ) + { + if ( index % itemsPerPage == 0 ) + { + PageIndex pageIndex = index / itemsPerPage; + value = arrayAllocator()->allocateArrayPage(); + pages_[pageIndex] = value; + } + new (value) Value( dereference( itOther ) ); + } +} + + +ValueInternalArray & +ValueInternalArray::operator =( const ValueInternalArray &other ) +{ + ValueInternalArray temp( other ); + swap( temp ); + return *this; +} + + +ValueInternalArray::~ValueInternalArray() +{ + // destroy all constructed items + IteratorState it; + IteratorState itEnd; + makeBeginIterator( it); + makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + value->~Value(); + } + // release all pages + PageIndex lastPageIndex = size_ / itemsPerPage; + for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex ) + arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); + // release pages index + arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ ); +} + + +void +ValueInternalArray::swap( ValueInternalArray &other ) +{ + Value **tempPages = pages_; + pages_ = other.pages_; + other.pages_ = tempPages; + ArrayIndex tempSize = size_; + size_ = other.size_; + other.size_ = tempSize; + PageIndex tempPageCount = pageCount_; + pageCount_ = other.pageCount_; + other.pageCount_ = tempPageCount; +} + +void +ValueInternalArray::clear() +{ + ValueInternalArray dummy; + swap( dummy ); +} + + +void +ValueInternalArray::resize( ArrayIndex newSize ) +{ + if ( newSize == 0 ) + clear(); + else if ( newSize < size_ ) + { + IteratorState it; + IteratorState itEnd; + makeIterator( it, newSize ); + makeIterator( itEnd, size_ ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + value->~Value(); + } + PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; + PageIndex lastPageIndex = size_ / itemsPerPage; + for ( ; pageIndex < lastPageIndex; ++pageIndex ) + arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); + size_ = newSize; + } + else if ( newSize > size_ ) + resolveReference( newSize ); +} + + +void +ValueInternalArray::makeIndexValid( ArrayIndex index ) +{ + // Need to enlarge page index ? + if ( index >= pageCount_ * itemsPerPage ) + { + PageIndex minNewPages = (index + 1) / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); + JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" ); + } + + // Need to allocate new pages ? + ArrayIndex nextPageIndex = + (size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage + : size_; + if ( nextPageIndex <= index ) + { + PageIndex pageIndex = nextPageIndex / itemsPerPage; + PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; + for ( ; pageToAllocate-- > 0; ++pageIndex ) + pages_[pageIndex] = arrayAllocator()->allocateArrayPage(); + } + + // Initialize all new entries + IteratorState it; + IteratorState itEnd; + makeIterator( it, size_ ); + size_ = index + 1; + makeIterator( itEnd, size_ ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + new (value) Value(); // Construct a default value using placement new + } +} + +Value & +ValueInternalArray::resolveReference( ArrayIndex index ) +{ + if ( index >= size_ ) + makeIndexValid( index ); + return pages_[index/itemsPerPage][index%itemsPerPage]; +} + +Value * +ValueInternalArray::find( ArrayIndex index ) const +{ + if ( index >= size_ ) + return 0; + return &(pages_[index/itemsPerPage][index%itemsPerPage]); +} + +ValueInternalArray::ArrayIndex +ValueInternalArray::size() const +{ + return size_; +} + +int +ValueInternalArray::distance( const IteratorState &x, const IteratorState &y ) +{ + return indexOf(y) - indexOf(x); +} + + +ValueInternalArray::ArrayIndex +ValueInternalArray::indexOf( const IteratorState &iterator ) +{ + if ( !iterator.array_ ) + return ArrayIndex(-1); + return ArrayIndex( + (iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage + + iterator.currentItemIndex_ ); +} + + +int +ValueInternalArray::compare( const ValueInternalArray &other ) const +{ + int sizeDiff( size_ - other.size_ ); + if ( sizeDiff != 0 ) + return sizeDiff; + + for ( ArrayIndex index =0; index < size_; ++index ) + { + int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare( + other.pages_[index/itemsPerPage][index%itemsPerPage] ); + if ( diff != 0 ) + return diff; + } + return 0; +} diff --git a/src/json/json_internalmap.inl b/src/json/json_internalmap.inl new file mode 100644 index 0000000..1977148 --- /dev/null +++ b/src/json/json_internalmap.inl @@ -0,0 +1,607 @@ +// included by json_value.cpp +// everything is within Json namespace + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalMap +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) ); + * This optimization is used by the fast allocator. + */ +ValueInternalLink::ValueInternalLink() + : previous_( 0 ) + , next_( 0 ) +{ +} + +ValueInternalLink::~ValueInternalLink() +{ + for ( int index =0; index < itemPerLink; ++index ) + { + if ( !items_[index].isItemAvailable() ) + { + if ( !items_[index].isMemberNameStatic() ) + free( keys_[index] ); + } + else + break; + } +} + + + +ValueMapAllocator::~ValueMapAllocator() +{ +} + +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueMapAllocator : public ValueMapAllocator +{ +public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } +}; +#else +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueMapAllocator : public ValueMapAllocator +{ +public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + ValueInternalMap *map = mapsAllocator_.allocate(); + new (map) ValueInternalMap(); // placement new + return map; + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + ValueInternalMap *map = mapsAllocator_.allocate(); + new (map) ValueInternalMap( other ); // placement new + return map; + } + + virtual void destructMap( ValueInternalMap *map ) + { + if ( map ) + { + map->~ValueInternalMap(); + mapsAllocator_.release( map ); + } + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + ValueInternalLink *link = linksAllocator_.allocate(); + memset( link, 0, sizeof(ValueInternalLink) ); + return link; + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + link->~ValueInternalLink(); + linksAllocator_.release( link ); + } +private: + BatchAllocator mapsAllocator_; + BatchAllocator linksAllocator_; +}; +#endif + +static ValueMapAllocator *&mapAllocator() +{ + static DefaultValueMapAllocator defaultAllocator; + static ValueMapAllocator *mapAllocator = &defaultAllocator; + return mapAllocator; +} + +static struct DummyMapAllocatorInitializer { + DummyMapAllocatorInitializer() + { + mapAllocator(); // ensure mapAllocator() statics are initialized before main(). + } +} dummyMapAllocatorInitializer; + + + +// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. + +/* +use linked list hash map. +buckets array is a container. +linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) +value have extra state: valid, available, deleted +*/ + + +ValueInternalMap::ValueInternalMap() + : buckets_( 0 ) + , tailLink_( 0 ) + , bucketsSize_( 0 ) + , itemCount_( 0 ) +{ +} + + +ValueInternalMap::ValueInternalMap( const ValueInternalMap &other ) + : buckets_( 0 ) + , tailLink_( 0 ) + , bucketsSize_( 0 ) + , itemCount_( 0 ) +{ + reserve( other.itemCount_ ); + IteratorState it; + IteratorState itEnd; + other.makeBeginIterator( it ); + other.makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + bool isStatic; + const char *memberName = key( it, isStatic ); + const Value &aValue = value( it ); + resolveReference(memberName, isStatic) = aValue; + } +} + + +ValueInternalMap & +ValueInternalMap::operator =( const ValueInternalMap &other ) +{ + ValueInternalMap dummy( other ); + swap( dummy ); + return *this; +} + + +ValueInternalMap::~ValueInternalMap() +{ + if ( buckets_ ) + { + for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex ) + { + ValueInternalLink *link = buckets_[bucketIndex].next_; + while ( link ) + { + ValueInternalLink *linkToRelease = link; + link = link->next_; + mapAllocator()->releaseMapLink( linkToRelease ); + } + } + mapAllocator()->releaseMapBuckets( buckets_ ); + } +} + + +void +ValueInternalMap::swap( ValueInternalMap &other ) +{ + ValueInternalLink *tempBuckets = buckets_; + buckets_ = other.buckets_; + other.buckets_ = tempBuckets; + ValueInternalLink *tempTailLink = tailLink_; + tailLink_ = other.tailLink_; + other.tailLink_ = tempTailLink; + BucketIndex tempBucketsSize = bucketsSize_; + bucketsSize_ = other.bucketsSize_; + other.bucketsSize_ = tempBucketsSize; + BucketIndex tempItemCount = itemCount_; + itemCount_ = other.itemCount_; + other.itemCount_ = tempItemCount; +} + + +void +ValueInternalMap::clear() +{ + ValueInternalMap dummy; + swap( dummy ); +} + + +ValueInternalMap::BucketIndex +ValueInternalMap::size() const +{ + return itemCount_; +} + +bool +ValueInternalMap::reserveDelta( BucketIndex growth ) +{ + return reserve( itemCount_ + growth ); +} + +bool +ValueInternalMap::reserve( BucketIndex newItemCount ) +{ + if ( !buckets_ && newItemCount > 0 ) + { + buckets_ = mapAllocator()->allocateMapBuckets( 1 ); + bucketsSize_ = 1; + tailLink_ = &buckets_[0]; + } +// BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; + return true; +} + + +const Value * +ValueInternalMap::find( const char *key ) const +{ + if ( !bucketsSize_ ) + return 0; + HashKey hashedKey = hash( key ); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for ( const ValueInternalLink *current = &buckets_[bucketIndex]; + current != 0; + current = current->next_ ) + { + for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( current->items_[index].isItemAvailable() ) + return 0; + if ( strcmp( key, current->keys_[index] ) == 0 ) + return ¤t->items_[index]; + } + } + return 0; +} + + +Value * +ValueInternalMap::find( const char *key ) +{ + const ValueInternalMap *constThis = this; + return const_cast( constThis->find( key ) ); +} + + +Value & +ValueInternalMap::resolveReference( const char *key, + bool isStatic ) +{ + HashKey hashedKey = hash( key ); + if ( bucketsSize_ ) + { + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink **previous = 0; + BucketIndex index; + for ( ValueInternalLink *current = &buckets_[bucketIndex]; + current != 0; + previous = ¤t->next_, current = current->next_ ) + { + for ( index=0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( current->items_[index].isItemAvailable() ) + return setNewItem( key, isStatic, current, index ); + if ( strcmp( key, current->keys_[index] ) == 0 ) + return current->items_[index]; + } + } + } + + reserveDelta( 1 ); + return unsafeAdd( key, isStatic, hashedKey ); +} + + +void +ValueInternalMap::remove( const char *key ) +{ + HashKey hashedKey = hash( key ); + if ( !bucketsSize_ ) + return; + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for ( ValueInternalLink *link = &buckets_[bucketIndex]; + link != 0; + link = link->next_ ) + { + BucketIndex index; + for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( link->items_[index].isItemAvailable() ) + return; + if ( strcmp( key, link->keys_[index] ) == 0 ) + { + doActualRemove( link, index, bucketIndex ); + return; + } + } + } +} + +void +ValueInternalMap::doActualRemove( ValueInternalLink *link, + BucketIndex index, + BucketIndex bucketIndex ) +{ + // find last item of the bucket and swap it with the 'removed' one. + // set removed items flags to 'available'. + // if last page only contains 'available' items, then desallocate it (it's empty) + ValueInternalLink *&lastLink = getLastLinkInBucket( index ); + BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 + for ( ; + lastItemIndex < ValueInternalLink::itemPerLink; + ++lastItemIndex ) // may be optimized with dicotomic search + { + if ( lastLink->items_[lastItemIndex].isItemAvailable() ) + break; + } + + BucketIndex lastUsedIndex = lastItemIndex - 1; + Value *valueToDelete = &link->items_[index]; + Value *valueToPreserve = &lastLink->items_[lastUsedIndex]; + if ( valueToDelete != valueToPreserve ) + valueToDelete->swap( *valueToPreserve ); + if ( lastUsedIndex == 0 ) // page is now empty + { // remove it from bucket linked list and delete it. + ValueInternalLink *linkPreviousToLast = lastLink->previous_; + if ( linkPreviousToLast != 0 ) // can not deleted bucket link. + { + mapAllocator()->releaseMapLink( lastLink ); + linkPreviousToLast->next_ = 0; + lastLink = linkPreviousToLast; + } + } + else + { + Value dummy; + valueToPreserve->swap( dummy ); // restore deleted to default Value. + valueToPreserve->setItemUsed( false ); + } + --itemCount_; +} + + +ValueInternalLink *& +ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex ) +{ + if ( bucketIndex == bucketsSize_ - 1 ) + return tailLink_; + ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_; + if ( !previous ) + previous = &buckets_[bucketIndex]; + return previous; +} + + +Value & +ValueInternalMap::setNewItem( const char *key, + bool isStatic, + ValueInternalLink *link, + BucketIndex index ) +{ + char *duplicatedKey = valueAllocator()->makeMemberName( key ); + ++itemCount_; + link->keys_[index] = duplicatedKey; + link->items_[index].setItemUsed(); + link->items_[index].setMemberNameIsStatic( isStatic ); + return link->items_[index]; // items already default constructed. +} + + +Value & +ValueInternalMap::unsafeAdd( const char *key, + bool isStatic, + HashKey hashedKey ) +{ + JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." ); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex ); + ValueInternalLink *link = previousLink; + BucketIndex index; + for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( link->items_[index].isItemAvailable() ) + break; + } + if ( index == ValueInternalLink::itemPerLink ) // need to add a new page + { + ValueInternalLink *newLink = mapAllocator()->allocateMapLink(); + index = 0; + link->next_ = newLink; + previousLink = newLink; + link = newLink; + } + return setNewItem( key, isStatic, link, index ); +} + + +ValueInternalMap::HashKey +ValueInternalMap::hash( const char *key ) const +{ + HashKey hash = 0; + while ( *key ) + hash += *key++ * 37; + return hash; +} + + +int +ValueInternalMap::compare( const ValueInternalMap &other ) const +{ + int sizeDiff( itemCount_ - other.itemCount_ ); + if ( sizeDiff != 0 ) + return sizeDiff; + // Strict order guaranty is required. Compare all keys FIRST, then compare values. + IteratorState it; + IteratorState itEnd; + makeBeginIterator( it ); + makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + if ( !other.find( key( it ) ) ) + return 1; + } + + // All keys are equals, let's compare values + makeBeginIterator( it ); + for ( ; !equals(it,itEnd); increment(it) ) + { + const Value *otherValue = other.find( key( it ) ); + int valueDiff = value(it).compare( *otherValue ); + if ( valueDiff != 0 ) + return valueDiff; + } + return 0; +} + + +void +ValueInternalMap::makeBeginIterator( IteratorState &it ) const +{ + it.map_ = const_cast( this ); + it.bucketIndex_ = 0; + it.itemIndex_ = 0; + it.link_ = buckets_; +} + + +void +ValueInternalMap::makeEndIterator( IteratorState &it ) const +{ + it.map_ = const_cast( this ); + it.bucketIndex_ = bucketsSize_; + it.itemIndex_ = 0; + it.link_ = 0; +} + + +bool +ValueInternalMap::equals( const IteratorState &x, const IteratorState &other ) +{ + return x.map_ == other.map_ + && x.bucketIndex_ == other.bucketIndex_ + && x.link_ == other.link_ + && x.itemIndex_ == other.itemIndex_; +} + + +void +ValueInternalMap::incrementBucket( IteratorState &iterator ) +{ + ++iterator.bucketIndex_; + JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_, + "ValueInternalMap::increment(): attempting to iterate beyond end." ); + if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ ) + iterator.link_ = 0; + else + iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); + iterator.itemIndex_ = 0; +} + + +void +ValueInternalMap::increment( IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." ); + ++iterator.itemIndex_; + if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink ) + { + JSON_ASSERT_MESSAGE( iterator.link_ != 0, + "ValueInternalMap::increment(): attempting to iterate beyond end." ); + iterator.link_ = iterator.link_->next_; + if ( iterator.link_ == 0 ) + incrementBucket( iterator ); + } + else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() ) + { + incrementBucket( iterator ); + } +} + + +void +ValueInternalMap::decrement( IteratorState &iterator ) +{ + if ( iterator.itemIndex_ == 0 ) + { + JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." ); + if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] ) + { + JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." ); + --(iterator.bucketIndex_); + } + iterator.link_ = iterator.link_->previous_; + iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; + } +} + + +const char * +ValueInternalMap::key( const IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + return iterator.link_->keys_[iterator.itemIndex_]; +} + +const char * +ValueInternalMap::key( const IteratorState &iterator, bool &isStatic ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); + return iterator.link_->keys_[iterator.itemIndex_]; +} + + +Value & +ValueInternalMap::value( const IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + return iterator.link_->items_[iterator.itemIndex_]; +} + + +int +ValueInternalMap::distance( const IteratorState &x, const IteratorState &y ) +{ + int offset = 0; + IteratorState it = x; + while ( !equals( it, y ) ) + increment( it ); + return offset; +} diff --git a/src/json/json_reader.cpp b/src/json/json_reader.cpp new file mode 100644 index 0000000..c0a863c --- /dev/null +++ b/src/json/json_reader.cpp @@ -0,0 +1,731 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() : + allowComments_(true), strictRoot_(false) { +} + +Features Features::all() { + return Features(); +} + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +static inline bool in(Reader::Char c, Reader::Char c1, Reader::Char c2, + Reader::Char c3, Reader::Char c4) { + return c == c1 || c == c2 || c == c3 || c == c4; +} + +static inline bool in(Reader::Char c, Reader::Char c1, Reader::Char c2, + Reader::Char c3, Reader::Char c4, Reader::Char c5) { + return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; +} + +static bool containsNewLine(Reader::Location begin, Reader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +static std::string codePointToUTF8(unsigned int cp) { + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); + result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() : + features_(Features::all()) { +} + +Reader::Reader(const Features &features) : + features_(features) { +} + +bool Reader::parse(const std::string &document, Value &root, + bool collectComments) { + document_ = document; + const char *begin = document_.c_str(); + const char *end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream& sin, Value &root, bool collectComments) { + //std::istream_iterator begin(sin); + //std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char) EOF); + return parse(doc, root, collectComments); +} + +bool Reader::parse(const char *beginDoc, const char *endDoc, Value &root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + break; + case tokenArrayBegin: + successful = readArray(token); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + currentValue() = true; + break; + case tokenFalse: + currentValue() = false; + break; + case tokenNull: + currentValue() = Value(); + break; + default: + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void Reader::skipCommentTokens(Token &token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::expectToken(TokenType type, Token &token, const char *message) { + readToken(token); + if (token.type_ != type) + return addError(message, token); + return true; +} + +bool Reader::readToken(Token &token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +void Reader::addComment(Location begin, Location end, + CommentPlacement placement) { + assert(collectComments_); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(std::string(begin, end), placement); + } else { + if (!commentsBefore_.empty()) + commentsBefore_ += "\n"; + commentsBefore_ += std::string(begin, end); + } +} + +bool Reader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\r' || c == '\n') + break; + } + return true; +} + +void Reader::readNumber() { + while (current_ != end_) { + if (!(*current_ >= '0' && *current_ <= '9') + && !in(*current_, '.', 'e', 'E', '+', '-')) + break; + ++current_; + } +} + +bool Reader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token &tokenStart) { + Token tokenName; + std::string name; + currentValue() = Value(objectValue); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + if (tokenName.type_ != tokenString) + break; + + name = ""; + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover("Missing ':' after object member name", + colon, tokenObjectEnd); + } + Value &value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) + || (comma.type_ != tokenObjectEnd + && comma.type_ != tokenArraySeparator + && comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, + tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); +} + +bool Reader::readArray(Token &tokenStart) { + currentValue() = Value(arrayValue); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + while (true) { + Value &value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = (token.type_ == tokenArraySeparator + && token.type_ == tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover("Missing ',' or ']' in array declaration", + token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token &token) { + bool isDouble = false; + for (Location inspect = token.start_; inspect != token.end_; ++inspect) { + isDouble = isDouble || in(*inspect, '.', 'e', 'E', '+') + || (*inspect == '-' && inspect != token.start_); + } + if (isDouble) + return decodeDouble(token); + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + Value::UInt threshold = ( + isNegative ? Value::UInt(-Value::minInt) : Value::maxUInt) / 10; + Value::UInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return addError( + "'" + std::string(token.start_, token.end_) + + "' is not a number.", token); + if (value >= threshold) + return decodeDouble(token); + value = value * 10 + Value::UInt(c - '0'); + } + if (isNegative) + currentValue() = -Value::Int(value); + else if (value <= Value::UInt(Value::maxInt)) + currentValue() = Value::Int(value); + else + currentValue() = value; + return true; +} + +bool Reader::decodeDouble(Token &token) { + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + if (length <= bufferSize) { + Char buffer[bufferSize]; + memcpy(buffer, token.start_, length); + buffer[length] = 0; + count = sscanf(buffer, "%lf", &value); + } else { + std::string buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), "%lf", &value); + } + + if (count != 1) + return addError( + "'" + std::string(token.start_, token.end_) + + "' is not a number.", token); + currentValue() = value; + return true; +} + +bool Reader::decodeString(Token &token) { + std::string decoded; + if (!decodeString(token, decoded)) + return false; + currentValue() = decoded; + return true; +} + +bool Reader::decodeString(Token &token, std::string &decoded) { + decoded.reserve(token.end_ - token.start_ - 2); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, + current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } + break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token &token, Location ¤t, + Location end, unsigned int &unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, + surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError( + "expecting another \\u token to begin the second half of a unicode surrogate pair", + token, current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token &token, Location ¤t, + Location end, unsigned int &unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, current); + unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, current); + } + return true; +} + +bool Reader::addError(const std::string &message, Token &token, + Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + int errorCount = int(errors_.size()); + Token skip; + while (true) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const std::string &message, Token &token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value & +Reader::currentValue() { + return *(nodes_.top()); +} + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, int &line, + int &column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +std::string Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + sprintf(buffer, "Line %d, Column %d", line, column); + return buffer; +} + +std::string Reader::getFormatedErrorMessages() const { + std::string formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); ++itError) { + const ErrorInfo &error = *itError; + formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += "See " + getLocationLineAndColumn(error.extra_) + + " for detail.\n"; + } + return formattedMessage; +} + +std::istream& operator>>(std::istream &sin, Value &root) { + Json::Reader reader; + bool ok = reader.parse(sin, root, true); + //JSON_ASSERT( ok ); + if (!ok) + throw std::runtime_error(reader.getFormatedErrorMessages()); + return sin; +} + +} // namespace Json diff --git a/src/json/json_value.cpp b/src/json/json_value.cpp new file mode 100644 index 0000000..17c8dc4 --- /dev/null +++ b/src/json/json_value.cpp @@ -0,0 +1,1418 @@ +#include +#include +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +# include +#endif +#include // size_t +#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +# include "json_batchallocator.h" +#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +#define JSON_ASSERT_UNREACHABLE assert( false ) +#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw +#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) throw std::runtime_error( message ); + +namespace Json { + +const Value Value::null; +const Int Value::minInt = Int(~(UInt(-1) / 2)); +const Int Value::maxInt = Int(UInt(-1) / 2); +const UInt Value::maxUInt = UInt(-1); + +// A "safe" implementation of strdup. Allow null pointer to be passed. +// Also avoid warning on msvc80. +// +//inline char *safeStringDup( const char *czstring ) +//{ +// if ( czstring ) +// { +// const size_t length = (unsigned int)( strlen(czstring) + 1 ); +// char *newString = static_cast( malloc( length ) ); +// memcpy( newString, czstring, length ); +// return newString; +// } +// return 0; +//} +// +//inline char *safeStringDup( const std::string &str ) +//{ +// if ( !str.empty() ) +// { +// const size_t length = str.length(); +// char *newString = static_cast( malloc( length + 1 ) ); +// memcpy( newString, str.c_str(), length ); +// newString[length] = 0; +// return newString; +// } +// return 0; +//} + +ValueAllocator::~ValueAllocator() { +} + +class DefaultValueAllocator: public ValueAllocator { +public: + virtual ~DefaultValueAllocator() { + } + + virtual char *makeMemberName(const char *memberName) { + return duplicateStringValue(memberName); + } + + virtual void releaseMemberName(char *memberName) { + releaseStringValue(memberName); + } + + virtual char *duplicateStringValue(const char *value, unsigned int length = + unknown) { + //@todo invesgate this old optimization + //if ( !value || value[0] == 0 ) + // return 0; + + if (length == unknown) + length = (unsigned int) strlen(value); + char *newString = static_cast(malloc(length + 1)); + memcpy(newString, value, length); + newString[length] = 0; + return newString; + } + + virtual void releaseStringValue(char *value) { + if (value) + free(value); + } +}; + +static ValueAllocator *&valueAllocator() { + static DefaultValueAllocator defaultAllocator; + static ValueAllocator *valueAllocator = &defaultAllocator; + return valueAllocator; +} + +static struct DummyValueAllocatorInitializer { + DummyValueAllocatorInitializer() { + valueAllocator(); // ensure valueAllocator() statics are initialized before main(). + } +} dummyValueAllocatorInitializer; + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#ifdef JSON_VALUE_USE_INTERNAL_MAP +# include "json_internalarray.inl" +# include "json_internalmap.inl" +#endif // JSON_VALUE_USE_INTERNAL_MAP +# include "json_valueiterator.inl" + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() : + comment_(0) { +} + +Value::CommentInfo::~CommentInfo() { + if (comment_) + valueAllocator()->releaseStringValue(comment_); +} + +void Value::CommentInfo::setComment(const char *text) { + if (comment_) + valueAllocator()->releaseStringValue(comment_); + JSON_ASSERT(text); + JSON_ASSERT_MESSAGE(text[0] == '\0' || text[0] == '/', + "Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = valueAllocator()->duplicateStringValue(text); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +# ifndef JSON_VALUE_USE_INTERNAL_MAP + +// Notes: index_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(int index) : + cstr_(0), index_(index) { +} + +Value::CZString::CZString(const char *cstr, DuplicationPolicy allocate) : + cstr_( + allocate == duplicate ? + valueAllocator()->makeMemberName(cstr) : cstr), index_( + allocate) { +} + +Value::CZString::CZString(const CZString &other) : + cstr_( + other.index_ != noDuplication && other.cstr_ != 0 ? + valueAllocator()->makeMemberName(other.cstr_) : + other.cstr_), index_( + other.cstr_ ? + (other.index_ == noDuplication ? + noDuplication : duplicate) : + other.index_) { +} + +Value::CZString::~CZString() { + if (cstr_ && index_ == duplicate) + valueAllocator()->releaseMemberName(const_cast(cstr_)); +} + +void Value::CZString::swap(CZString &other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString & +Value::CZString::operator =(const CZString &other) { + CZString temp(other); + swap(temp); + return *this; +} + +bool Value::CZString::operator<(const CZString &other) const { + if (cstr_) + return strcmp(cstr_, other.cstr_) < 0; + return index_ < other.index_; +} + +bool Value::CZString::operator==(const CZString &other) const { + if (cstr_) + return strcmp(cstr_, other.cstr_) == 0; + return index_ == other.index_; +} + +int Value::CZString::index() const { + return index_; +} + +const char * +Value::CZString::c_str() const { + return cstr_; +} + +bool Value::CZString::isStaticString() const { + return index_ == noDuplication; +} + +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType type) : + type_(type), allocated_(0), comments_(0) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + switch (type) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArray(); + break; + case objectValue: + value_.map_ = mapAllocator()->newMap(); + break; +#endif + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) : + type_(intValue), comments_(0) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + value_.int_ = value; +} + +Value::Value(UInt value) : + type_(uintValue), comments_(0) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + value_.uint_ = value; +} + +Value::Value(double value) : + type_(realValue), comments_(0) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + value_.real_ = value; +} + +Value::Value(const char *value) : + type_(stringValue), allocated_(true), comments_(0) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue(value); +} + +Value::Value(const char *beginValue, const char *endValue) : + type_(stringValue), allocated_(true), comments_(0) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue(beginValue, + UInt(endValue - beginValue)); +} + +Value::Value(const std::string &value) : + type_(stringValue), allocated_(true), comments_(0) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue(value.c_str(), + (unsigned int) value.length()); + +} + +Value::Value(const StaticString &value) : + type_(stringValue), allocated_(false), comments_(0) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + value_.string_ = const_cast(value.c_str()); +} + +# ifdef JSON_USE_CPPTL +Value::Value( const CppTL::ConstString &value ) +: type_( stringValue ) +, allocated_( true ) +, comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue( value, value.length() ); +} +# endif + +Value::Value(bool value) : + type_(booleanValue), comments_(0) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + value_.bool_ = value; +} + +Value::Value(const Value &other) : + type_(other.type_), comments_(0) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +, itemIsUsed_( 0 ) +#endif +{ + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_) { + value_.string_ = valueAllocator()->duplicateStringValue( + other.value_.string_); + allocated_ = true; + } else + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); + break; + case objectValue: + value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + if (other.comments_) { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { + const CommentInfo &otherComment = other.comments_[comment]; + if (otherComment.comment_) + comments_[comment].setComment(otherComment.comment_); + } + } +} + +Value::~Value() { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (allocated_) + valueAllocator()->releaseStringValue(value_.string_); + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + delete value_.map_; + break; +#else + case arrayValue: + arrayAllocator()->destructArray( value_.array_ ); + break; + case objectValue: + mapAllocator()->destructMap( value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + + if (comments_) + delete[] comments_; +} + +Value & +Value::operator=(const Value &other) { + Value temp(other); + swap(temp); + return *this; +} + +void Value::swap(Value &other) { + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap(value_, other.value_); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2; +} + +ValueType Value::type() const { + return type_; +} + +int Value::compare(const Value &other) { + /* + int typeDelta = other.type_ - type_; + switch ( type_ ) + { + case nullValue: + + return other.type_ == type_; + case intValue: + if ( other.type_.isNumeric() + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue, + break; + case arrayValue: + delete value_.array_; + break; + case objectValue: + delete value_.map_; + default: + JSON_ASSERT_UNREACHABLE; + } + */ + return 0; // unreachable +} + +bool Value::operator <(const Value &other) const { + int typeDelta = type_ - other.type_; + if (typeDelta) + return typeDelta < 0 ? true : false; + switch (type_) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + return (value_.string_ == 0 && other.value_.string_) + || (other.value_.string_ && value_.string_ + && strcmp(value_.string_, other.value_.string_) < 0); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: { + int delta = int(value_.map_->size() - other.value_.map_->size()); + if (delta) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) < 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) < 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable +} + +bool Value::operator <=(const Value &other) const { + return !(other > *this); +} + +bool Value::operator >=(const Value &other) const { + return !(*this < other); +} + +bool Value::operator >(const Value &other) const { + return other < *this; +} + +bool Value::operator ==(const Value &other) const { + //if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if (type_ != temp) + return false; + switch (type_) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + return (value_.string_ == other.value_.string_) + || (other.value_.string_ && value_.string_ + && strcmp(value_.string_, other.value_.string_) == 0); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() + && (*value_.map_) == (*other.value_.map_); +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) == 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) == 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable +} + +bool Value::operator !=(const Value &other) const { + return !(*this == other); +} + +const char * +Value::asCString() const { + JSON_ASSERT(type_ == stringValue); + return value_.string_; +} + +std::string Value::asString() const { + switch (type_) { + case nullValue: + return ""; + case stringValue: + return value_.string_ ? value_.string_ : ""; + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + case uintValue: + case realValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE(false, "Type is not convertible to string") + ; + default: + JSON_ASSERT_UNREACHABLE; + } + return ""; // unreachable +} + +# ifdef JSON_USE_CPPTL +CppTL::ConstString +Value::asConstString() const +{ + return CppTL::ConstString( asString().c_str() ); +} +# endif + +Value::Int Value::asInt() const { + switch (type_) { + case nullValue: + return 0; + case intValue: + return value_.int_; + case uintValue: + JSON_ASSERT_MESSAGE(value_.uint_ < (unsigned )maxInt, + "integer out of signed integer range") + ; + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE(value_.real_ >= minInt && value_.real_ <= maxInt, + "Real out of signed integer range") + ; + return Int(value_.real_); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE(false, "Type is not convertible to int") + ; + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +Value::UInt Value::asUInt() const { + switch (type_) { + case nullValue: + return 0; + case intValue: + JSON_ASSERT_MESSAGE(value_.int_ >= 0, + "Negative integer can not be converted to unsigned integer") + ; + return value_.int_; + case uintValue: + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE(value_.real_ >= 0 && value_.real_ <= maxUInt, + "Real out of unsigned integer range") + ; + return UInt(value_.real_); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE(false, "Type is not convertible to uint") + ; + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +double Value::asDouble() const { + switch (type_) { + case nullValue: + return 0.0; + case intValue: + return value_.int_; + case uintValue: + return value_.uint_; + case realValue: + return value_.real_; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE(false, "Type is not convertible to double") + ; + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +bool Value::asBool() const { + switch (type_) { + case nullValue: + return false; + case intValue: + case uintValue: + return value_.int_ != 0; + case realValue: + return value_.real_ != 0.0; + case booleanValue: + return value_.bool_; + case stringValue: + return value_.string_ && value_.string_[0] != 0; + case arrayValue: + case objectValue: + return value_.map_->size() != 0; + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable; +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (type_) { + case nullValue: + return true; + case intValue: + return (other == nullValue && value_.int_ == 0) || other == intValue + || (other == uintValue && value_.int_ >= 0) + || other == realValue || other == stringValue + || other == booleanValue; + case uintValue: + return (other == nullValue && value_.uint_ == 0) + || (other == intValue && value_.uint_ <= (unsigned) maxInt) + || other == uintValue || other == realValue + || other == stringValue || other == booleanValue; + case realValue: + return (other == nullValue && value_.real_ == 0.0) + || (other == intValue && value_.real_ >= minInt + && value_.real_ <= maxInt) + || (other == uintValue && value_.real_ >= 0 + && value_.real_ <= maxUInt) || other == realValue + || other == stringValue || other == booleanValue; + case booleanValue: + return (other == nullValue && value_.bool_ == false) + || other == intValue || other == uintValue || other == realValue + || other == stringValue || other == booleanValue; + case stringValue: + return other == stringValue + || (other == nullValue + && (!value_.string_ || value_.string_[0] == 0)); + case arrayValue: + return other == arrayValue + || (other == nullValue && value_.map_->size() == 0); + case objectValue: + return other == objectValue + || (other == nullValue && value_.map_->size() == 0); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable; +} + +/// Number of values in array or object +Value::UInt Value::size() const { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return Int(value_.map_->size()); +#else + case arrayValue: + return Int( value_.array_->size() ); + case objectValue: + return Int( value_.map_->size() ); +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +bool Value::operator!() const { + return isNull(); +} + +void Value::clear() { + JSON_ASSERT( + type_ == nullValue || type_ == arrayValue || type_ == objectValue); + + switch (type_) { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_->clear(); + break; +#else + case arrayValue: + value_.array_->clear(); + break; + case objectValue: + value_.map_->clear(); + break; +#endif + default: + break; + } +} + +void Value::resize(UInt newSize) { + JSON_ASSERT(type_ == nullValue || type_ == arrayValue); + if (type_ == nullValue) + *this = Value(arrayValue); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + UInt oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + (*this)[newSize - 1]; + else { + for (UInt index = newSize; index < oldSize; ++index) + value_.map_->erase(index); + assert(size() == newSize); + } +#else + value_.array_->resize( newSize ); +#endif +} + +Value & +Value::operator[](UInt index) { + JSON_ASSERT(type_ == nullValue || type_ == arrayValue); + if (type_ == nullValue) + *this = Value(arrayValue); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key(index); + ObjectValues::iterator it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, null); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +#else + return value_.array_->resolveReference( index ); +#endif +} + +const Value & +Value::operator[](UInt index) const { + JSON_ASSERT(type_ == nullValue || type_ == arrayValue); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return null; + return (*it).second; +#else + Value *value = value_.array_->find( index ); + return value ? *value : null; +#endif +} + +Value & +Value::operator[](const char *key) { + return resolveReference(key, false); +} + +Value & +Value::resolveReference(const char *key, bool isStatic) { + JSON_ASSERT(type_ == nullValue || type_ == objectValue); + if (type_ == nullValue) + *this = Value(objectValue); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey(key, + isStatic ? CZString::noDuplication : CZString::duplicateOnCopy); + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, null); + it = value_.map_->insert(it, defaultValue); + Value &value = (*it).second; + return value; +#else + return value_.map_->resolveReference( key, isStatic ); +#endif +} + +Value Value::get(UInt index, const Value &defaultValue) const { + const Value *value = &((*this)[index]); + return value == &null ? defaultValue : *value; +} + +bool Value::isValidIndex(UInt index) const { + return index < size(); +} + +const Value & +Value::operator[](const char *key) const { + JSON_ASSERT(type_ == nullValue || type_ == objectValue); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey(key, CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return null; + return (*it).second; +#else + const Value *value = value_.map_->find( key ); + return value ? *value : null; +#endif +} + +Value & +Value::operator[](const std::string &key) { + return (*this)[key.c_str()]; +} + +const Value & +Value::operator[](const std::string &key) const { + return (*this)[key.c_str()]; +} + +Value & +Value::operator[](const StaticString &key) { + return resolveReference(key, true); +} + +# ifdef JSON_USE_CPPTL +Value & +Value::operator[]( const CppTL::ConstString &key ) +{ + return (*this)[ key.c_str() ]; +} + +const Value & +Value::operator[]( const CppTL::ConstString &key ) const +{ + return (*this)[ key.c_str() ]; +} +# endif + +Value & +Value::append(const Value &value) { + return (*this)[size()] = value; +} + +Value Value::get(const char *key, const Value &defaultValue) const { + const Value *value = &((*this)[key]); + return value == &null ? defaultValue : *value; +} + +Value Value::get(const std::string &key, const Value &defaultValue) const { + return get(key.c_str(), defaultValue); +} + +Value Value::removeMember(const char* key) { + JSON_ASSERT(type_ == nullValue || type_ == objectValue); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey(key, CZString::noDuplication); + ObjectValues::iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return null; + Value old(it->second); + value_.map_->erase(it); + return old; +#else + Value *value = value_.map_->find( key ); + if (value) { + Value old(*value); + value_.map_.remove( key ); + return old; + } else { + return null; + } +#endif +} + +Value Value::removeMember(const std::string &key) { + return removeMember(key.c_str()); +} + +# ifdef JSON_USE_CPPTL +Value +Value::get( const CppTL::ConstString &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} +# endif + +bool Value::isMember(const char *key) const { + const Value *value = &((*this)[key]); + return value != &null; +} + +bool Value::isMember(const std::string &key) const { + return isMember(key.c_str()); +} + +# ifdef JSON_USE_CPPTL +bool +Value::isMember( const CppTL::ConstString &key ) const +{ + return isMember( key.c_str() ); +} +#endif + +Value::Members Value::getMemberNames() const { + JSON_ASSERT(type_ == nullValue || type_ == objectValue); + if (type_ == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) + members.push_back(std::string((*it).first.c_str())); +#else + ValueInternalMap::IteratorState it; + ValueInternalMap::IteratorState itEnd; + value_.map_->makeBeginIterator( it ); + value_.map_->makeEndIterator( itEnd ); + for (; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) + members.push_back( std::string( ValueInternalMap::key( it ) ) ); +#endif + return members; +} +// +//# ifdef JSON_USE_CPPTL +//EnumMemberNames +//Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +//EnumValues +//Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + +bool Value::isNull() const { + return type_ == nullValue; +} + +bool Value::isBool() const { + return type_ == booleanValue; +} + +bool Value::isInt() const { + return type_ == intValue; +} + +bool Value::isUInt() const { + return type_ == uintValue; +} + +bool Value::isIntegral() const { + return type_ == intValue || type_ == uintValue || type_ == booleanValue; +} + +bool Value::isDouble() const { + return type_ == realValue; +} + +bool Value::isNumeric() const { + return isIntegral() || isDouble(); +} + +bool Value::isString() const { + return type_ == stringValue; +} + +bool Value::isArray() const { + return type_ == nullValue || type_ == arrayValue; +} + +bool Value::isObject() const { + return type_ == nullValue || type_ == objectValue; +} + +void Value::setComment(const char *comment, CommentPlacement placement) { + if (!comments_) + comments_ = new CommentInfo[numberOfCommentPlacement]; + comments_[placement].setComment(comment); +} + +void Value::setComment(const std::string &comment, CommentPlacement placement) { + setComment(comment.c_str(), placement); +} + +bool Value::hasComment(CommentPlacement placement) const { + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string Value::getComment(CommentPlacement placement) const { + if (hasComment(placement)) + return comments_[placement].comment_; + return ""; +} + +std::string Value::toStyledString() const { + StyledWriter writer; + return writer.write(*this); +} + +Value::const_iterator Value::begin() const { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::const_iterator Value::end() const { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::iterator Value::begin() { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; +#endif + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; +#endif + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() : + kind_(kindNone) { +} + +PathArgument::PathArgument(Value::UInt index) : + index_(index), kind_(kindIndex) { +} + +PathArgument::PathArgument(const char *key) : + key_(key), kind_(kindKey) { +} + +PathArgument::PathArgument(const std::string &key) : + key_(key.c_str()), kind_(kindKey) { +} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const std::string &path, const PathArgument &a1, + const PathArgument &a2, const PathArgument &a3, const PathArgument &a4, + const PathArgument &a5) { + InArgs in; + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const std::string &path, const InArgs &in) { + const char *current = path.c_str(); + const char *end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + Value::UInt index = 0; + for (; current != end && *current >= '0' && *current <= '9'; + ++current) + index = index * 10 + Value::UInt(*current - '0'); + args_.push_back(index); + } + if (current == end || *current++ != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.') { + ++current; + } else { + const char *beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(std::string(beginName, current)); + } + } +} + +void Path::addPathInArg(const std::string &path, const InArgs &in, + InArgs::const_iterator &itInArg, PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg); + } +} + +void Path::invalidPath(const std::string &path, int location) { + // Error: invalid path. +} + +const Value & +Path::resolve(const Value &root) const { + const Value *node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument &arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if (node == &Value::null) { + // Error: unable to resolve path (object has no member named '' at position...) + } + } + } + return *node; +} + +Value Path::resolve(const Value &root, const Value &defaultValue) const { + const Value *node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument &arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::null) + return defaultValue; + } + } + return *node; +} + +Value & +Path::make(Value &root) const { + Value *node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument &arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json diff --git a/src/json/json_valueiterator.inl b/src/json/json_valueiterator.inl new file mode 100644 index 0000000..736e260 --- /dev/null +++ b/src/json/json_valueiterator.inl @@ -0,0 +1,292 @@ +// included by json_value.cpp +// everything is within Json namespace + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() +#ifndef JSON_VALUE_USE_INTERNAL_MAP + : current_() + , isNull_( true ) +{ +} +#else + : isArray_( true ) + , isNull_( true ) +{ + iterator_.array_ = ValueInternalArray::IteratorState(); +} +#endif + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) + : current_( current ) + , isNull_( false ) +{ +} +#else +ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) + : isArray_( true ) +{ + iterator_.array_ = state; +} + + +ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) + : isArray_( false ) +{ + iterator_.map_ = state; +} +#endif + +Value & +ValueIteratorBase::deref() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + return current_->second; +#else + if ( isArray_ ) + return ValueInternalArray::dereference( iterator_.array_ ); + return ValueInternalMap::value( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::increment() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ++current_; +#else + if ( isArray_ ) + ValueInternalArray::increment( iterator_.array_ ); + ValueInternalMap::increment( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::decrement() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + --current_; +#else + if ( isArray_ ) + ValueInternalArray::decrement( iterator_.array_ ); + ValueInternalMap::decrement( iterator_.map_ ); +#endif +} + + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP +# ifdef JSON_USE_CPPTL_SMALLMAP + return current_ - other.current_; +# else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if ( isNull_ && other.isNull_ ) + { + return 0; + } + + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) + { + ++myDistance; + } + return myDistance; +# endif +#else + if ( isArray_ ) + return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +bool +ValueIteratorBase::isEqual( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + if ( isNull_ ) + { + return other.isNull_; + } + return current_ == other.current_; +#else + if ( isArray_ ) + return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::copy( const SelfType &other ) +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + current_ = other.current_; +#else + if ( isArray_ ) + iterator_.array_ = other.iterator_.array_; + iterator_.map_ = other.iterator_.map_; +#endif +} + + +Value +ValueIteratorBase::key() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( czstring.c_str() ) + { + if ( czstring.isStaticString() ) + return Value( StaticString( czstring.c_str() ) ); + return Value( czstring.c_str() ); + } + return Value( czstring.index() ); +#else + if ( isArray_ ) + return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); + bool isStatic; + const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); + if ( isStatic ) + return Value( StaticString( memberName ) ); + return Value( memberName ); +#endif +} + + +UInt +ValueIteratorBase::index() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( !czstring.c_str() ) + return czstring.index(); + return Value::UInt( -1 ); +#else + if ( isArray_ ) + return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); + return Value::UInt( -1 ); +#endif +} + + +const char * +ValueIteratorBase::memberName() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const char *name = (*current_).first.c_str(); + return name ? name : ""; +#else + if ( !isArray_ ) + return ValueInternalMap::key( iterator_.map_ ); + return ""; +#endif +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueConstIterator & +ValueConstIterator::operator =( const ValueIteratorBase &other ) +{ + copy( other ); + return *this; +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueIterator::ValueIterator( const ValueConstIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator::ValueIterator( const ValueIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator & +ValueIterator::operator =( const SelfType &other ) +{ + copy( other ); + return *this; +} diff --git a/src/json/json_writer.cpp b/src/json/json_writer.cpp new file mode 100644 index 0000000..2179e5a --- /dev/null +++ b/src/json/json_writer.cpp @@ -0,0 +1,675 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +static bool isControlCharacter(char ch) { + return ch > 0 && ch <= 0x1F; +} + +static bool containsControlCharacter(const char* str) { + while (*str) { + if (isControlCharacter(*(str++))) + return true; + } + return false; +} +static void uintToString(unsigned int value, char *¤t) { + *--current = 0; + do { + *--current = (value % 10) + '0'; + value /= 10; + } while (value != 0); +} + +std::string valueToString(Int value) { + char buffer[32]; + char *current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if (isNegative) + value = -value; + uintToString(UInt(value), current); + if (isNegative) + *--current = '-'; + assert(current >= buffer); + return current; +} + +std::string valueToString(UInt value) { + char buffer[32]; + char *current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +std::string valueToString(double value) { + char buffer[32]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. + sprintf_s(buffer, sizeof(buffer), "%#.16g", value); +#else + sprintf(buffer, "%#.16g", value); +#endif + char* ch = buffer + strlen(buffer) - 1; + if (*ch != '0') + return buffer; // nothing to truncate, so save time + while (ch > buffer && *ch == '0') { + --ch; + } + char* last_nonzero = ch; + while (ch >= buffer) { + switch (*ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + --ch; + continue; + case '.': + // Truncate zeroes to save bytes in output, but keep one. + *(last_nonzero + 2) = '\0'; + return buffer; + default: + return buffer; + } + } + return buffer; +} + +std::string valueToString(bool value) { + return value ? "true" : "false"; +} + +std::string valueToQuotedString(const char *value) { + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL + && !containsControlCharacter(value)) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + unsigned maxsize = strlen(value) * 2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c = value; *c != 0; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + //case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() { +} + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() : + yamlCompatiblityEnabled_(false) { +} + +void FastWriter::enableYAMLCompatibility() { + yamlCompatiblityEnabled_ = true; +} + +std::string FastWriter::write(const Value &root) { + document_ = ""; + writeValue(root); + document_ += "\n"; + return document_; +} + +void FastWriter::writeValue(const Value &value) { + switch (value.type()) { + case nullValue: + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asInt()); + break; + case uintValue: + document_ += valueToString(value.asUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: + document_ += valueToQuotedString(value.asCString()); + break; + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += "["; + int size = value.size(); + for (int index = 0; index < size; ++index) { + if (index > 0) + document_ += ","; + writeValue(value[index]); + } + document_ += "]"; + } + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += "{"; + for (Value::Members::iterator it = members.begin(); it != members.end(); + ++it) { + const std::string &name = *it; + if (it != members.begin()) + document_ += ","; + document_ += valueToQuotedString(name.c_str()); + document_ += yamlCompatiblityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += "}"; + } + break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() : + rightMargin_(74), indentSize_(3) { +} + +std::string StyledWriter::write(const Value &root) { + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + +void StyledWriter::writeValue(const Value &value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asInt())); + break; + case uintValue: + pushValue(valueToString(value.asUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + pushValue(valueToQuotedString(value.asCString())); + break; + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + while (true) { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } + break; + } +} + +void StyledWriter::writeArrayValue(const Value &value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + while (true) { + const Value &childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultineArray(const Value &value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value &childValue = value[index]; + isMultiLine = isMultiLine + || ((childValue.isArray() || childValue.isObject()) + && childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size && !isMultiLine; ++index) { + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + isMultiLine = isMultiLine && hasCommentForValue(value[index]); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const std::string &value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const std::string &value) { + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() { + indentString_ += std::string(indentSize_, ' '); +} + +void StyledWriter::unindent() { + assert(int(indentString_.size()) >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value &root) { + if (!root.hasComment(commentBefore)) + return; + document_ += normalizeEOL(root.getComment(commentBefore)); + document_ += "\n"; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value &root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + + normalizeEOL(root.getComment(commentAfterOnSameLine)); + + if (root.hasComment(commentAfter)) { + document_ += "\n"; + document_ += normalizeEOL(root.getComment(commentAfter)); + document_ += "\n"; + } +} + +bool StyledWriter::hasCommentForValue(const Value &value) { + return value.hasComment(commentBefore) + || value.hasComment(commentAfterOnSameLine) + || value.hasComment(commentAfter); +} + +std::string StyledWriter::normalizeEOL(const std::string &text) { + std::string normalized; + normalized.reserve(text.length()); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') // mac or dos EOL + { + if (*current == '\n') // convert dos EOL + ++current; + normalized += '\n'; + } else + // handle unix EOL & other char + normalized += c; + } + return normalized; +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(std::string indentation) : + document_(NULL), rightMargin_(74), indentation_(indentation) { +} + +void StyledStreamWriter::write(std::ostream &out, const Value &root) { + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value &value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asInt())); + break; + case uintValue: + pushValue(valueToString(value.asUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + pushValue(valueToQuotedString(value.asCString())); + break; + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + while (true) { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } + break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value &value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + while (true) { + const Value &childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultineArray(const Value &value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value &childValue = value[index]; + isMultiLine = isMultiLine + || ((childValue.isArray() || childValue.isObject()) + && childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size && !isMultiLine; ++index) { + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + isMultiLine = isMultiLine && hasCommentForValue(value[index]); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const std::string &value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + /* + Some comments in this method would have been nice. ;-) + + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + *document_ << '\n'; + } + */ + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const std::string &value) { + writeIndent(); + *document_ << value; +} + +void StyledStreamWriter::indent() { + indentString_ += indentation_; +} + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value &root) { + if (!root.hasComment(commentBefore)) + return; + *document_ << normalizeEOL(root.getComment(commentBefore)); + *document_ << "\n"; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value &root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ + << " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + + if (root.hasComment(commentAfter)) { + *document_ << "\n"; + *document_ << normalizeEOL(root.getComment(commentAfter)); + *document_ << "\n"; + } +} + +bool StyledStreamWriter::hasCommentForValue(const Value &value) { + return value.hasComment(commentBefore) + || value.hasComment(commentAfterOnSameLine) + || value.hasComment(commentAfter); +} + +std::string StyledStreamWriter::normalizeEOL(const std::string &text) { + std::string normalized; + normalized.reserve(text.length()); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') // mac or dos EOL + { + if (*current == '\n') // convert dos EOL + ++current; + normalized += '\n'; + } else + // handle unix EOL & other char + normalized += c; + } + return normalized; +} + +std::ostream& operator<<(std::ostream &sout, const Value &root) { + Json::StyledStreamWriter writer; + writer.write(sout, root); + return sout; +} + +} // namespace Json diff --git a/src/json/sconscript b/src/json/sconscript new file mode 100644 index 0000000..f6520d1 --- /dev/null +++ b/src/json/sconscript @@ -0,0 +1,8 @@ +Import( 'env buildLibrary' ) + +buildLibrary( env, Split( """ + json_reader.cpp + json_value.cpp + json_writer.cpp + """ ), + 'json' ) diff --git a/src/main/main.cpp b/src/main/main.cpp new file mode 100644 index 0000000..f8e7bf6 --- /dev/null +++ b/src/main/main.cpp @@ -0,0 +1,55 @@ +#include +#include + +#include "httpserver.h" + +int gServerStatus = 0; +int gIsRun = 0; + +//parse the cmd line parameters +void parse(int count, char *argv[], HttpServer *httpserver) { + for (int i = 1; i < count; ++i) { + string argvstr = argv[i]; + if (argvstr == "--debug"){ + httpserver->g_show_log = true; + continue; + } + int sepindex = argvstr.find(":"); + if (sepindex > -1) { + string key = argvstr.substr(0, sepindex); + string value = argvstr.substr(sepindex + 1); + if (key == "--port") { + httpserver->g_port = value; + } else if (key == "--hidestatus") { + httpserver->g_hide_status = value; + } else if (key == "--testsuite") { + httpserver->g_test_suite = value; + } else if (key == "--exe_sequence") { + httpserver->g_exe_sequence = value; + } else if (key == "--enable_memory_collection") { + httpserver->g_enable_memory_collection = value; + } else if (key == "--external-test") { + //parse the value to lancher and index_file + httpserver->g_launcher = value; + if (value == "wrt-launcher") + { + httpserver->g_run_wiget = true; + httpserver->g_launch_cmd = httpserver->g_launcher + " -s " + httpserver->g_test_suite; + httpserver->g_kill_cmd = httpserver->g_launcher + " -k " + httpserver->g_test_suite; + } + else + httpserver->g_run_wiget = false; + } + } + } +} + +int main(int argc, char *argv[]) { + HttpServer httpserver; + if (argc > 1) { + parse(argc, argv, &httpserver); + } + httpserver.StartUp(); + return 0; +} + diff --git a/src/testcase.cpp b/src/testcase.cpp new file mode 100644 index 0000000..1c942cf --- /dev/null +++ b/src/testcase.cpp @@ -0,0 +1,75 @@ +#include +#include +#include + +#include "testcase.h" + +using namespace std; + +TestCase::TestCase() { + result = "N/A"; + + std_out = ""; + + is_executed = false; +} + +TestCase::~TestCase() { +} + +void TestCase::init(const Json::Value value) { + m_case = value; + purpose = value["purpose"].asString(); // server will use this string directly + case_id = value["case_id"].asString(); + if (value["timeout"].isString()) + timeout_value = atoi(value["timeout"].asString().c_str()); + else timeout_value = 90; +} + +Json::Value TestCase::to_json() { + return m_case; +} + +Json::Value TestCase::result_to_json() { + Json::Value root; + + root["order"] = m_case["order"]; + root["case_id"] = m_case["case_id"]; + root["result"] = result; + + if (std_out != "") { + root["stdout"] = std_out; + root["start_at"] = start_at; + root["end_at"] = end_at; + } + + return root; +} + +void TestCase::set_result(string test_result, string test_msg) { + is_executed = true; + + result = test_result; + + std_out = test_msg; + + getCurrentTime(); + end_at = m_str_time; +} + +void TestCase::set_start_at() { + getCurrentTime(); + cout << "\nstart time: " << m_str_time << endl; + start_at = m_str_time; +} + +void TestCase::getCurrentTime() { + memset(m_str_time, 0, 32); + time_t timer; + struct tm* t_tm; + time(&timer); + t_tm = localtime(&timer); + sprintf(m_str_time, "%4d-%02d-%02d %02d:%02d:%02d", t_tm->tm_year + 1900, + t_tm->tm_mon + 1, t_tm->tm_mday, t_tm->tm_hour, t_tm->tm_min, + t_tm->tm_sec); +} \ No newline at end of file diff --git a/src/ut/httpserver_log.txt b/src/ut/httpserver_log.txt new file mode 100644 index 0000000..5fe4a2f --- /dev/null +++ b/src/ut/httpserver_log.txt @@ -0,0 +1,290 @@ + +[ testing xml: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.xml ] + +[ split xml: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1.xml by ] +[ this might take some time, please wait ] +[ total set number is: 10 ] +[ process set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_1.xml ] +[ process set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_2.xml ] +[ process set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_3.xml ] +[ process set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_4.xml ] +[ process set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_5.xml ] +[ process set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_6.xml ] +[ process set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_7.xml ] +[ process set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_8.xml ] +[ process set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_9.xml ] +[ process set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_10.xml ] +[ remove empty set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_1.xml ] +[ remove empty set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_2.xml ] +[ remove empty set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_3.xml ] +[ remove empty set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_4.xml ] +[ remove empty set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_5.xml ] +[ remove empty set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_6.xml ] +[ remove empty set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_7.xml ] +[ remove empty set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_8.xml ] +[ remove empty set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_9.xml ] + +[ run set: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_10.xml ] +[ split xml: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_10.xml by ] +[ this might take some time, please wait ] +[ prepare_starup_parameters ] +[ waiting for kill http server ] +[ forward server http://127.0.0.1:9002 ] +[ launch the stub app ] +httpserver->g_test_suite is: api2contt0 +[Server is running.....] +wrt-launcher: no process found +[ check server status, get ready! ] +GET /check_server_status HTTP/1.1 +Host: 127.0.0.1:9002 +Content-Length: 0 +Accept-Encoding: gzip, deflate, compress +Accept: */* +User-Agent: python-requests/1.1.0 CPython/2.7.3 Linux/3.5.0-27-generic + + +block: 1/-1215935884, total case: 0/0, block case: 0/0, m_timeout_count:0 +server response is:{ + "block_finished" : 0, + "finished" : 0 +} + +POST /init_test HTTP/1.1 +Host: 127.0.0.1:9002 +Content-Length: 587 +content-type: application/json +Accept-Encoding: gzip, deflate, compress +Accept: */* +User-Agent: python-requests/1.1.0 CPython/2.7.3 Linux/3.5.0-27-generic + +{"currentBlk": "1", "totalBlk": "1", "casecount": "1", "cases": [{"onload_delay": "3", "case_id": "video_addTextTrack_base", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the vedio.addTextTrack has all arguments that expected a new MutableTextTrack object is to be created and returned"}], "purpose": "Check if the vedio.addTextTrack has all arguments that expected a new MutableTextTrack object is to be created and returned", "entry": "/opt/tct-webapi-w3c-content-tests/Video/video_addTextTrack_base.html", "order": "1"}], "type": "compliance", "exetype": "auto"} +[ init the test suite ] +wrt-launcher -k api2contt0 +result: App isn't running + +[ test suite: tct-webapi-w3c-content-tests.auto.suite_1_set_10.xml, block: 1/1 , finished: 0 ] +wrt-launcher -s api2contt0 +result: launched + +server response is:{"OK":1} +GET /check_server_status HTTP/1.1 +Host: 127.0.0.1:9002 +Content-Length: 0 +Accept-Encoding: gzip, deflate, compress +Accept: */* +User-Agent: python-requests/1.1.0 CPython/2.7.3 Linux/3.5.0-27-generic + + +block: 1/1, total case: 0/1, block case: 0/1, m_timeout_count:0 +server response is:{ + "block_finished" : 0, + "finished" : 0 +} + +[ test suite: tct-webapi-w3c-content-tests.auto.suite_1_set_10.xml, block: 1/1 , finished: 0 ] +GET /check_server_status HTTP/1.1 +Host: 127.0.0.1:9002 +Content-Length: 0 +Accept-Encoding: gzip, deflate, compress +Accept: */* +User-Agent: python-requests/1.1.0 CPython/2.7.3 Linux/3.5.0-27-generic + + +block: 1/1, total case: 0/1, block case: 0/1, m_timeout_count:0 +server response is:{ + "block_finished" : 0, + "finished" : 0 +} + +GET /check_server HTTP/1.1 +Host: 127.0.0.1:8000 +Accept-Language: en +User-Agent: Mozilla/5.0 (Linux; U; Tizen 2.0; en-us) AppleWebKit/537.1 (KHTML, like Gecko) Version/2.0 Mobile +Accept: */* +Accept-Charset: iso-8859-1, utf-8, utf16, *;q=0.1 +x-Wap-Proxy-Cookie: none +Accept-Encoding: gzip, deflate +Connection: Keep-Alive + + +[ checking server, and found the server is running ] +server response is:{"OK":1} +GET /init_session_id?session_id=6330 HTTP/1.1 +Host: 127.0.0.1:8000 +Accept-Language: en +User-Agent: Mozilla/5.0 (Linux; U; Tizen 2.0; en-us) AppleWebKit/537.1 (KHTML, like Gecko) Version/2.0 Mobile +Accept: */* +Accept-Charset: iso-8859-1, utf-8, utf16, *;q=0.1 +x-Wap-Proxy-Cookie: none +Accept-Encoding: gzip, deflate +Connection: Keep-Alive + + +[ sessionID: 6330 is gotten from the client ] +server response is:{"OK":1} +GET /auto_test_task?session_id=6330 HTTP/1.1 +Host: 127.0.0.1:8000 +Accept-Language: en +User-Agent: Mozilla/5.0 (Linux; U; Tizen 2.0; en-us) AppleWebKit/537.1 (KHTML, like Gecko) Version/2.0 Mobile +Accept: text/plain, */*; q=0.01 +Accept-Charset: iso-8859-1, utf-8, utf16, *;q=0.1 +x-Wap-Proxy-Cookie: none +Accept-Encoding: gzip, deflate +Connection: Keep-Alive + + + +start time: 1982-02-06 15:27:38 + +[case] execute case: video_addTextTrack_base +last_test_result: N/A +server response is:{ + "case_id" : "video_addTextTrack_base", + "entry" : "/opt/tct-webapi-w3c-content-tests/Video/video_addTextTrack_base.html", + "onload_delay" : "3", + "order" : "1", + "purpose" : "Check if the vedio.addTextTrack has all arguments that expected a new MutableTextTrack object is to be created and returned", + "steps" : [ + { + "expected" : "Pass", + "order" : "1", + "step_desc" : "Check if the vedio.addTextTrack has all arguments that expected a new MutableTextTrack object is to be created and returned" + } + ] +} + +GET /check_execution_progress?session_id=6330 HTTP/1.1 +Host: 127.0.0.1:8000 +Accept-Language: en +User-Agent: Mozilla/5.0 (Linux; U; Tizen 2.0; en-us) AppleWebKit/537.1 (KHTML, like Gecko) Version/2.0 Mobile +Accept: text/plain, */*; q=0.01 +Accept-Charset: iso-8859-1, utf-8, utf16, *;q=0.1 +x-Wap-Proxy-Cookie: none +Accept-Encoding: gzip, deflate +Connection: Keep-Alive + + +server response is:{"total":1,"current":1,"last_test_result":"N/A"} +GET /ask_next_step?session_id=6330 HTTP/1.1 +Host: 127.0.0.1:8000 +Accept-Language: en +User-Agent: Mozilla/5.0 (Linux; U; Tizen 2.0; en-us) AppleWebKit/537.1 (KHTML, like Gecko) Version/2.0 Mobile +Accept: text/plain, */*; q=0.01 +Accept-Charset: iso-8859-1, utf-8, utf16, *;q=0.1 +x-Wap-Proxy-Cookie: none +Accept-Encoding: gzip, deflate +Connection: Keep-Alive + + +server response is:{"step":"continue"} +POST /commit_result HTTP/1.1 +Host: 127.0.0.1:8000 +Origin: file:// +Accept-Language: en +User-Agent: Mozilla/5.0 (Linux; U; Tizen 2.0; en-us) AppleWebKit/537.1 (KHTML, like Gecko) Version/2.0 Mobile +Content-Type: application/x-www-form-urlencoded +Accept: application/json, text/javascript, */*; q=0.01 +Accept-Charset: iso-8859-1, utf-8, utf16, *;q=0.1 +x-Wap-Proxy-Cookie: none +Accept-Encoding: gzip, deflate +Connection: Keep-Alive +Content-Length: 177 + +purpose=Check+if+the+vedio.addTextTrack+has+all+arguments+that+expected+a+new+MutableTextTrack+object+is+to+be+created+and+returned&result=PASS&msg=%5BMessage%5D&session_id=6330 +server response is:{"OK":1} +GET /auto_test_task?session_id=6330 HTTP/1.1 +Host: 127.0.0.1:8000 +Accept-Language: en +User-Agent: Mozilla/5.0 (Linux; U; Tizen 2.0; en-us) AppleWebKit/537.1 (KHTML, like Gecko) Version/2.0 Mobile +Accept: text/plain, */*; q=0.01 +Accept-Charset: iso-8859-1, utf-8, utf16, *;q=0.1 +x-Wap-Proxy-Cookie: none +Accept-Encoding: gzip, deflate +Connection: Keep-Alive + + + +[ no auto case is available any more ] +server response is:{"none":0} +GET /manual_cases HTTP/1.1 +Host: 127.0.0.1:8000 +Accept-Language: en +User-Agent: Mozilla/5.0 (Linux; U; Tizen 2.0; en-us) AppleWebKit/537.1 (KHTML, like Gecko) Version/2.0 Mobile +Accept: text/plain, */*; q=0.01 +Accept-Charset: iso-8859-1, utf-8, utf16, *;q=0.1 +x-Wap-Proxy-Cookie: none +Accept-Encoding: gzip, deflate +Connection: Keep-Alive + + +server response is:{"none":0} +[ test suite: tct-webapi-w3c-content-tests.auto.suite_1_set_10.xml, block: 1/1 , finished: 1 ] +GET /check_server_status HTTP/1.1 +Host: 127.0.0.1:9002 +Content-Length: 0 +Accept-Encoding: gzip, deflate, compress +Accept: */* +User-Agent: python-requests/1.1.0 CPython/2.7.3 Linux/3.5.0-27-generic + + +block: 1/1, total case: 1/1, block case: 1/1, m_timeout_count:0 +server response is:{ + "block_finished" : 1, + "finished" : 1 +} + +GET /get_test_result HTTP/1.1 +Host: 127.0.0.1:9002 +Content-Length: 0 +Accept-Encoding: gzip, deflate, compress +Accept: */* +User-Agent: python-requests/1.1.0 CPython/2.7.3 Linux/3.5.0-27-generic + + +server response is:{ + "cases" : [ + { + "case_id" : "video_addTextTrack_base", + "end_at" : "1982-02-06 15:27:39", + "order" : "1", + "result" : "PASS", + "start_at" : "1982-02-06 15:27:38", + "stdout" : "[Message]" + } + ], + "count" : "1" +} + +[ cases result saved to resultfile ] + +[ show down server ] +GET /shut_down_server HTTP/1.1 +Host: 127.0.0.1:9002 +Content-Length: 0 +Accept-Encoding: gzip, deflate, compress +Accept: */* +User-Agent: python-requests/1.1.0 CPython/2.7.3 Linux/3.5.0-27-generic + + +wrt-launcher: no process found + +[ test complete at time: 2013-04-18_16_06_12 ] +[ start merging test result xml files, this might take some time, please wait ] +[ merge result files into /opt/testkit/lite/2013-04-18-16:05:48.429618/tests.result.xml ] +|--[ merge webapi result file: /opt/testkit/lite/2013-04-18-16:05:48.429618/tct-webapi-w3c-content-tests.auto.suite_1_set_10.xml ] +----[ suite: tct-webapi-w3c-content-tests, set: Video, time: 2013-04-18_16_06_12 ] +[ test summary ] + [ total case number: 1 ] + [ pass rate: 100.00% ] + [ PASS case number: 1 ] + [ FAIL case number: 0 ] + [ BLOCK case number: 0 ] + [ N/A case number: 0 ] +[ generate result xml: /opt/testkit/lite/2013-04-18-16:05:48.429618/tests.result.xml ] +[ merge complete, write to the result file, this might take some time, please wait ] +wrt-launcher -k api2contt0 +result: killed + +server response is:{"OK":1} +[ all tasks for testkit lite are accomplished, goodbye ] +danny@danny-Latitude-E6400:~$ diff --git a/src/ut/test.json b/src/ut/test.json new file mode 100644 index 0000000..b01c641 --- /dev/null +++ b/src/ut/test.json @@ -0,0 +1 @@ +{"currentBlk": "4", "totalBlk": "4", "casecount": "387", "cases": [{"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint16Array_setter.html", "pre_condition": "none", "case_id": "Uint16Array_setter", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify setter of Uint16Array"}], "purpose": "Verify setter of Uint16Array", "order": "301"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint16Array_set_array.html", "pre_condition": "none", "case_id": "Uint16Array_set_array", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(array) of Uint16Array"}], "purpose": "Verify set(array) of Uint16Array", "order": "302"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint16Array_set_array_offset.html", "pre_condition": "none", "case_id": "Uint16Array_set_array_offset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(array, offset) of Uint16Array"}], "purpose": "Verify set(array, offset) of Uint16Array", "order": "303"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint16Array_set_exist.html", "pre_condition": "none", "case_id": "Uint16Array_set_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the set of Uint16Array exist"}], "purpose": "Check if the set of Uint16Array exist", "order": "304"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint16Array_set_TypedArray.html", "pre_condition": "none", "case_id": "Uint16Array_set_TypedArray", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(typedarray) of Uint16Array"}], "purpose": "Verify set(typedarray) of Uint16Array", "order": "305"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint16Array_set_TypedArray_offset.html", "pre_condition": "none", "case_id": "Uint16Array_set_TypedArray_offset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(typedarray, offset) of Uint16Array"}], "purpose": "Verify set(typedarray, offset) of Uint16Array", "order": "306"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint16Array_subarray_begin.html", "pre_condition": "none", "case_id": "Uint16Array_subarray_begin", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify subarray(begin) of Uint16Array"}], "purpose": "Verify subarray(begin) of Uint16Array", "order": "307"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint16Array_subarray_begin_end.html", "pre_condition": "none", "case_id": "Uint16Array_subarray_begin_end", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify subarray(begin, end) of Uint16Array"}], "purpose": "Verify subarray(begin, end) of Uint16Array", "order": "308"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint16Array_subarray_exist.html", "pre_condition": "none", "case_id": "Uint16Array_subarray_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the subarray of Uint16Array exist"}], "purpose": "Check if the subarray of Uint16Array exist", "order": "309"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_buffer.html", "pre_condition": "none", "case_id": "Uint32Array_buffer", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify buffer of Uint32Array"}], "purpose": "Verify buffer of Uint32Array", "order": "310"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_buffer_exist.html", "pre_condition": "none", "case_id": "Uint32Array_buffer_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the buffer of Uint32Array exist"}], "purpose": "Check if the buffer of Uint32Array exist", "order": "311"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_byteLength.html", "pre_condition": "none", "case_id": "Uint32Array_byteLength", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify byteLength of Uint32Array"}], "purpose": "Verify byteLength of Uint32Array", "order": "312"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_byteLength_exist.html", "pre_condition": "none", "case_id": "Uint32Array_byteLength_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the byteLength of Uint32Array exist"}], "purpose": "Check if the byteLength of Uint32Array exist", "order": "313"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_byteOffset.html", "pre_condition": "none", "case_id": "Uint32Array_byteOffset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify byteOffset of Uint32Array"}], "purpose": "Verify byteOffset of Uint32Array", "order": "314"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_byteOffset_exist.html", "pre_condition": "none", "case_id": "Uint32Array_byteOffset_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the byteOffset of Uint32Array exist"}], "purpose": "Check if the byteOffset of Uint32Array exist", "order": "315"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_BYTES_PER_ELEMENT_const_4.html", "pre_condition": "none", "case_id": "Uint32Array_BYTES_PER_ELEMENT_const_4", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify BYTES_PER_ELEMENT of Uint32Array is 4"}], "purpose": "Verify BYTES_PER_ELEMENT of Uint32Array is 4", "order": "316"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_BYTES_PER_ELEMENT_exist.html", "pre_condition": "none", "case_id": "Uint32Array_BYTES_PER_ELEMENT_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the BYTES_PER_ELEMENT of Uint32Array exist"}], "purpose": "Check if the BYTES_PER_ELEMENT of Uint32Array exist", "order": "317"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_constructor_array.html", "pre_condition": "none", "case_id": "Uint32Array_constructor_array", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(array) of Uint32Array"}], "purpose": "Verify constructor(array) of Uint32Array", "order": "318"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_constructor_ArrayBuffer.html", "pre_condition": "none", "case_id": "Uint32Array_constructor_ArrayBuffer", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(arraybuffer) of Uint32Array"}], "purpose": "Verify constructor(arraybuffer) of Uint32Array", "order": "319"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_constructor_ArrayBuffer_byteOffset.html", "pre_condition": "none", "case_id": "Uint32Array_constructor_ArrayBuffer_byteOffset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(arraybuffer, byteOffset) of Uint32Array"}], "purpose": "Verify constructor(arraybuffer, byteOffset) of Uint32Array", "order": "320"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_constructor_ArrayBuffer_byteOffset_length.html", "pre_condition": "none", "case_id": "Uint32Array_constructor_ArrayBuffer_byteOffset_length", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(arraybuffer, byteOffset, length) of Uint32Array"}], "purpose": "Verify constructor(arraybuffer, byteOffset, length) of Uint32Array", "order": "321"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_constructor_length.html", "pre_condition": "none", "case_id": "Uint32Array_constructor_length", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(length) of Uint32Array"}], "purpose": "Verify constructor(length) of Uint32Array", "order": "322"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_constructor_TypedArray.html", "pre_condition": "none", "case_id": "Uint32Array_constructor_TypedArray", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(typedarray) of Uint32Array"}], "purpose": "Verify constructor(typedarray) of Uint32Array", "order": "323"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_getter.html", "pre_condition": "none", "case_id": "Uint32Array_getter", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify getter of Uint32Array"}], "purpose": "Verify getter of Uint32Array", "order": "324"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_length.html", "pre_condition": "none", "case_id": "Uint32Array_length", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify length of Uint32Array"}], "purpose": "Verify length of Uint32Array", "order": "325"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_length_exist.html", "pre_condition": "none", "case_id": "Uint32Array_length_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the length of Uint32Array exist"}], "purpose": "Check if the length of Uint32Array exist", "order": "326"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_setter.html", "pre_condition": "none", "case_id": "Uint32Array_setter", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify setter of Uint32Array"}], "purpose": "Verify setter of Uint32Array", "order": "327"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_set_array.html", "pre_condition": "none", "case_id": "Uint32Array_set_array", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(array) of Uint32Array"}], "purpose": "Verify set(array) of Uint32Array", "order": "328"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_set_array_offset.html", "pre_condition": "none", "case_id": "Uint32Array_set_array_offset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(array, offset) of Uint32Array"}], "purpose": "Verify set(array, offset) of Uint32Array", "order": "329"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_set_exist.html", "pre_condition": "none", "case_id": "Uint32Array_set_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the set of Uint32Array exist"}], "purpose": "Check if the set of Uint32Array exist", "order": "330"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_set_TypedArray.html", "pre_condition": "none", "case_id": "Uint32Array_set_TypedArray", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(typedarray) of Uint32Array"}], "purpose": "Verify set(typedarray) of Uint32Array", "order": "331"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_set_TypedArray_offset.html", "pre_condition": "none", "case_id": "Uint32Array_set_TypedArray_offset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(typedarray, offset) of Uint32Array"}], "purpose": "Verify set(typedarray, offset) of Uint32Array", "order": "332"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_subarray_begin.html", "pre_condition": "none", "case_id": "Uint32Array_subarray_begin", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify subarray(begin) of Uint32Array"}], "purpose": "Verify subarray(begin) of Uint32Array", "order": "333"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_subarray_begin_end.html", "pre_condition": "none", "case_id": "Uint32Array_subarray_begin_end", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(begin, end) of Uint32Array"}], "purpose": "Verify set(begin, end) of Uint32Array", "order": "334"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint32Array_subarray_exist.html", "pre_condition": "none", "case_id": "Uint32Array_subarray_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the subarray of Uint32Array exist"}], "purpose": "Check if the subarray of Uint32Array exist", "order": "335"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_buffer.html", "pre_condition": "none", "case_id": "Uint8Array_buffer", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the buffer of Uint8Array"}], "purpose": "Verify the buffer of Uint8Array", "order": "336"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_buffer_exist.html", "pre_condition": "none", "case_id": "Uint8Array_buffer_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the buffer of Uint8Array exist"}], "purpose": "Check if the buffer of Uint8Array exist", "order": "337"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_byteLength.html", "pre_condition": "none", "case_id": "Uint8Array_byteLength", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the byteLength of Uint8Array"}], "purpose": "Verify the byteLength of Uint8Array", "order": "338"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_byteLength_exist.html", "pre_condition": "none", "case_id": "Uint8Array_byteLength_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the byteLength of Uint8Array exist"}], "purpose": "Check if the byteLength of Uint8Array exist", "order": "339"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_byteOffset.html", "pre_condition": "none", "case_id": "Uint8Array_byteOffset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the byteOffset of Uint8Array"}], "purpose": "Verify the byteOffset of Uint8Array", "order": "340"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_byteOffset_exist.html", "pre_condition": "none", "case_id": "Uint8Array_byteOffset_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the byteOffset of Uint8Array exist"}], "purpose": "Check if the byteOffset of Uint8Array exist", "order": "341"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_BYTES_PER_ELEMENT_const_1.html", "pre_condition": "none", "case_id": "Uint8Array_BYTES_PER_ELEMENT_const_1", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the BYTES_PER_ELEMENT of Uint8Array is 1"}], "purpose": "Verify the BYTES_PER_ELEMENT of Uint8Array is 1", "order": "342"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_BYTES_PER_ELEMENT_exist.html", "pre_condition": "none", "case_id": "Uint8Array_BYTES_PER_ELEMENT_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the BYTES_PER_ELEMENT of Uint8Array exist"}], "purpose": "Check if the BYTES_PER_ELEMENT of Uint8Array exist", "order": "343"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_constructor_array.html", "pre_condition": "none", "case_id": "Uint8Array_constructor_array", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the constructor(array) of Uint8Array"}], "purpose": "Verify the constructor(array) of Uint8Array", "order": "344"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_constructor_ArrayBuffer.html", "pre_condition": "none", "case_id": "Uint8Array_constructor_ArrayBuffer", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the constructor(arraybuffer) of Uint8Array"}], "purpose": "Verify the constructor(arraybuffer) of Uint8Array", "order": "345"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_constructor_ArrayBuffer_byteOffSet.html", "pre_condition": "none", "case_id": "Uint8Array_constructor_ArrayBuffer_byteOffSet", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the constructor(arraybuffer, byteOffset) of Uint8Array"}], "purpose": "Verify the constructor(arraybuffer, byteOffset) of Uint8Array", "order": "346"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_constructor_ArrayBuffer_byteOffSet_length.html", "pre_condition": "none", "case_id": "Uint8Array_constructor_ArrayBuffer_byteOffSet_length", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the constructor(arraybuffer, byteOffset, length) of Uint8Array"}], "purpose": "Verify the constructor(arraybuffer, byteOffset, length) of Uint8Array", "order": "347"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_constructor_length.html", "pre_condition": "none", "case_id": "Uint8Array_constructor_length", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the constructor(length) of Uint8Array"}], "purpose": "Verify the constructor(length) of Uint8Array", "order": "348"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_constructor_TypedArray.html", "pre_condition": "none", "case_id": "Uint8Array_constructor_TypedArray", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the constructor(typedarray) of Uint8Array"}], "purpose": "Verify the constructor(typedarray) of Uint8Array", "order": "349"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_getter.html", "pre_condition": "none", "case_id": "Uint8Array_getter", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the getter of Uint8Array"}], "purpose": "Verify the getter of Uint8Array", "order": "350"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_length.html", "pre_condition": "none", "case_id": "Uint8Array_length", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the length of Uint8Array"}], "purpose": "Verify the length of Uint8Array", "order": "351"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_length_exist.html", "pre_condition": "none", "case_id": "Uint8Array_length_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the length of Uint8Array exist"}], "purpose": "Check if the length of Uint8Array exist", "order": "352"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_setter.html", "pre_condition": "none", "case_id": "Uint8Array_setter", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the setter of Uint8Array"}], "purpose": "Verify the setter of Uint8Array", "order": "353"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_set_array.html", "pre_condition": "none", "case_id": "Uint8Array_set_array", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the set(array) of Uint8Array"}], "purpose": "Verify the set(array) of Uint8Array", "order": "354"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_set_array_offset.html", "pre_condition": "none", "case_id": "Uint8Array_set_array_offset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the set(array, offset) of Uint8Array"}], "purpose": "Verify the set(array, offset) of Uint8Array", "order": "355"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_set_exist.html", "pre_condition": "none", "case_id": "Uint8Array_set_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the set of Uint8Array exist"}], "purpose": "Check if the set of Uint8Array exist", "order": "356"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_set_TypedArray.html", "pre_condition": "none", "case_id": "Uint8Array_set_TypedArray", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the set(typedarray) of Uint8Array"}], "purpose": "Verify the set(typedarray) of Uint8Array", "order": "357"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_set_TypedArray_offset.html", "pre_condition": "none", "case_id": "Uint8Array_set_TypedArray_offset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the set(typedarray, offset) of Uint8Array"}], "purpose": "Verify the set(typedarray, offset) of Uint8Array", "order": "358"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_subarray_begin.html", "pre_condition": "none", "case_id": "Uint8Array_subarray_begin", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the subarray(begin) of Uint8Array"}], "purpose": "Verify the subarray(begin) of Uint8Array", "order": "359"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_subarray_begin_end.html", "pre_condition": "none", "case_id": "Uint8Array_subarray_begin_end", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the subarray(begin, end) of Uint8Array"}], "purpose": "Verify the subarray(begin, end) of Uint8Array", "order": "360"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8Array_subarray_exist.html", "pre_condition": "none", "case_id": "Uint8Array_subarray_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the subarray of Uint8Array exist"}], "purpose": "Check if the subarray of Uint8Array exist", "order": "361"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_buffer.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_buffer", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the buffer of Uint8ClampedArray"}], "purpose": "Verify the buffer of Uint8ClampedArray", "order": "362"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_buffer_exist.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_buffer_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "check if the buffer of Uint8ClampedArray exist"}], "purpose": "check if the buffer of Uint8ClampedArray exist", "order": "363"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_byteLength.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_byteLength", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the byteLength of Uint8ClampedArray"}], "purpose": "Verify the byteLength of Uint8ClampedArray", "order": "364"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_byteLength_exist.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_byteLength_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the byteLength of Uint8ClampedArray exist"}], "purpose": "Check if the byteLength of Uint8ClampedArray exist", "order": "365"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_byteOffset.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_byteOffset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the byteOffset of Uint8ClampedArray"}], "purpose": "Verify the byteOffset of Uint8ClampedArray", "order": "366"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_byteOffset_exist.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_byteOffset_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "check if the byteOffset of Uint8ClampedArray exist"}], "purpose": "check if the byteOffset of Uint8ClampedArray exist", "order": "367"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_BYTES_PER_ELEMENT_const_1.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_BYTES_PER_ELEMENT_const_1", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify the BYTES_PER-ELEMENT of Uint8ClampedArray is 1"}], "purpose": "Verify the BYTES_PER-ELEMENT of Uint8ClampedArray is 1", "order": "368"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_BYTES_PER_ELEMENT_exist.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_BYTES_PER_ELEMENT_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the BYTES_PER_ELEMENT of Uint8ClampedArray exist"}], "purpose": "Check if the BYTES_PER_ELEMENT of Uint8ClampedArray exist", "order": "369"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_constructor_array.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_constructor_array", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(array) of Uint8ClampedArray"}], "purpose": "Verify constructor(array) of Uint8ClampedArray", "order": "370"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_constructor_ArrayBuffer.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_constructor_ArrayBuffer", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(arraybuffer) of Uint8ClampedArray"}], "purpose": "Verify constructor(arraybuffer) of Uint8ClampedArray", "order": "371"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_constructor_ArrayBuffer_byteOffSet.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_constructor_ArrayBuffer_byteOffSet", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(arraybuffer, byteOffset) of Uint8ClampedArray"}], "purpose": "Verify constructor(arraybuffer, byteOffset) of Uint8ClampedArray", "order": "372"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_constructor_ArrayBuffer_byteOffSet_length.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_constructor_ArrayBuffer_byteOffSet_length", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(buffer, byteOffset, length) of Uint8ClampedArray"}], "purpose": "Verify constructor(buffer, byteOffset, length) of Uint8ClampedArray", "order": "373"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_constructor_length.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_constructor_length", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(length) of Uint8ClampedArray"}], "purpose": "Verify constructor(length) of Uint8ClampedArray", "order": "374"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_constructor_TypedArray.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_constructor_TypedArray", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify constructor(typedarray) of Uint8ClampedArray"}], "purpose": "Verify constructor(typedarray) of Uint8ClampedArray", "order": "375"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_getter.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_getter", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify getter of Uint8ClampedArray"}], "purpose": "Verify getter of Uint8ClampedArray", "order": "376"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_length.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_length", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify length of Uint8ClampedArray"}], "purpose": "Verify length of Uint8ClampedArray", "order": "377"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_length_exist.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_length_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the length of Uint8ClampedArray exist"}], "purpose": "Check if the length of Uint8ClampedArray exist", "order": "378"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_setter.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_setter", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify setter of Uint8ClampedArray"}], "purpose": "Verify setter of Uint8ClampedArray", "order": "379"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_set_array.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_set_array", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(array) of Uint8ClampedArray"}], "purpose": "Verify set(array) of Uint8ClampedArray", "order": "380"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_set_array_offset.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_set_array_offset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(array, offset) of Uint8ClampedArray"}], "purpose": "Verify set(array, offset) of Uint8ClampedArray", "order": "381"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_set_exist.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_set_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "check if the set of Uint8ClampedArray exist"}], "purpose": "check if the set of Uint8ClampedArray exist", "order": "382"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_set_TypedArray.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_set_TypedArray", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(typedarray) of Uint8ClampedArray"}], "purpose": "Verify set(typedarray) of Uint8ClampedArray", "order": "383"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_set_TypedArray_offset.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_set_TypedArray_offset", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify set(typedarray, offset) of Uint8ClampedArray"}], "purpose": "Verify set(typedarray, offset) of Uint8ClampedArray", "order": "384"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_subarray_begin.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_subarray_begin", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify subarray(begin) of Uint8ClampedArray"}], "purpose": "Verify subarray(begin) of Uint8ClampedArray", "order": "385"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_subarray_begin_end.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_subarray_begin_end", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Verify subarray(begin, end) of Uint8ClampedArray"}], "purpose": "Verify subarray(begin, end) of Uint8ClampedArray", "order": "386"}, {"post_condition": "none", "onload_delay": "3", "test_script_entry": "/opt/tct-webapi-nonw3c-tests/TypedArrays/Uint8ClampedArray_subarray_exist.html", "pre_condition": "none", "case_id": "Uint8ClampedArray_subarray_exist", "steps": [{"expected": "Pass", "order": "1", "step_desc": "Check if the subarray of Uint8ClampedArray exist"}], "purpose": "Check if the subarray of Uint8ClampedArray exist", "order": "387"}], "type": "compliance", "exetype": "auto"} \ No newline at end of file diff --git a/src/ut/ut.cpp b/src/ut/ut.cpp new file mode 100644 index 0000000..e581afe --- /dev/null +++ b/src/ut/ut.cpp @@ -0,0 +1,83 @@ +#include "httpserver.h" + +int main() { + HttpServer* httpserver = new HttpServer(); + + httpserver->parse_json_str("src/ut/test.json"); + + struct HttpRequest httprequest; + + httprequest.path = "/init_test"; + httpserver->processpost(1, &httprequest); + + httpserver->g_test_suite = "api3nonw3c"; + + httprequest.path = "/check_server"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/check_server_status"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/shut_down_server"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/ask_next_step"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/init_session_id?session_id=1024"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/auto_test_task?session_id=1024"; + httprequest.content = "session_id=1024"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/manual_cases"; + httpserver->processpost(1, &httprequest); + + httprequest.content = "purpose=ut-cas&result=N/A"; + httprequest.path = "/commit_manual_result"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/check_execution_progress"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/generate_xml"; + httpserver->processpost(1, &httprequest); + + httprequest.content = "purpose=Verify setter of Uint16Array&result=N/A"; + httprequest.path = "/commit_result"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/check_execution_progress"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/set_capability"; + httprequest.content = "{\"name1\":true, \"name2\":45, \"name3\":\"678\"}"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/capability"; + httprequest.content = "name=name1"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/capability?name=name2&value=45"; + httprequest.content = "name=name2&value=45"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/capability?name=name3&value=678"; + httprequest.content = "name=name3&value=678"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/capability?name=name4"; + httprequest.content = "name=name4"; + httpserver->processpost(1, &httprequest); + + httprequest.path = "/set_capability"; + httprequest.content = "{\"bluetooth\":true, \"nfc\":true, \"multiTouchCount\":true, \"inputKeyboard\":true, \"wifi\":true, \"wifiDirect\":true, \"openglesVersion1_1\":true, \"openglesVersion2_0\":true, \"fmRadio\":true, \"platformVersion\":true, \"webApiVersion\":true, \"nativeApiVersion\":true, \"platformName\":true, \"cameraFront\":true, \"cameraFrontFlash\":true, \"cameraBack\":true, \"cameraBackFlash\":true, \"location\":true, \"locationGps\":true, \"locationWps\":true, \"microphone\":true, \"usbHost\":true, \"usbAccessory\":true, \"screenOutputRca\":true, \"screenOutputHdmi\":true, \"platformCoreCpuArch\":true, \"platformCoreFpuArch\":true, \"sipVoip\":true, \"duid\":true, \"speechRecognition\":true, \"accelerometer\":true, \"barometer\":true, \"gyroscope\":true, \"magnetometer\":true, \"proximity\":true}"; + httpserver->processpost(1, &httprequest); + + httpserver->StartUp(); + + delete httpserver; + + return 0; +}