diff options
27 files changed, 6101 insertions, 4 deletions
diff --git a/include/m_json.h b/include/m_json.h new file mode 100644 index 0000000000..76c9d966f6 --- /dev/null +++ b/include/m_json.h @@ -0,0 +1,234 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2008 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef MIM_LIBJSON_H
+#define MIM_LIBJSON_H
+
+#define IsJSONServiceAvailable() (ServiceExists(MS_JSON_FREE))
+#define json_getJSI(a) CallService(MS_JSON_GETINTERFACE,(WPARAM)a,0)
+
+#ifndef LIBJSON_H
+ #define JSONNODE void //so that JSONNODE* is void* + typedef JSONNODE** JSONNODE_ITERATOR; +#endif
+
+// This function get the service interface for direct calling
+// wParam=(WPARAM)(LPJSONSERVICEINTERFACE)
+// lParam=0
+// Return value ALWAYS 0
+
+typedef struct {
+ /* + stuff that's in namespace libJSON + */ + void (*free)(void * str); + void (*delete_)(JSONNODE * node); + // #ifdef JSON_MEMORY_MANAGE + void (*free_all)(void); + void (*delete_all)(void); + // #endif + JSONNODE * (*parse)(const char * json); + char * (*strip_white_space)(const char * json); + // #ifdef JSON_VALIDATE + JSONNODE * (*validate)(const char * json); + // #endif + + /* + stuff that's in class JSONNode + */ + //ctors + JSONNODE * (*new_a)(const char * name, const char * value); + JSONNODE * (*new_i)(const char * name, long value); + JSONNODE * (*new_f)(const char * name, double value); + JSONNODE * (*new_b)(const char * name, int value); //because C bools are ints and C++ will implicitly cast it + JSONNODE * (*new_)(char type); + JSONNODE * (*copy)(const JSONNODE * orig); + JSONNODE * (*duplicate)(const JSONNODE * orig); + + //assignment + void (*set_a)(JSONNODE * node, const char * value); + void (*set_i)(JSONNODE * node, long value); + void (*set_f)(JSONNODE * node, double value); + void (*set_b)(JSONNODE * node, int value); //because C bools are ints ane C++ will implicit + void (*set_n)(JSONNODE * node, const JSONNODE * orig); + + //inspectors + char (*type)(const JSONNODE * node); + unsigned int (*size)(const JSONNODE * node); + int (*empty)(const JSONNODE * node); + char * (*name)(const JSONNODE * node); + // #ifdef JSON_COMMENTS + char * (*get_comment)(const JSONNODE * node); + // #endif + char * (*as_string)(const JSONNODE * node); + long (*as_int)(const JSONNODE * node); + double (*as_float)(const JSONNODE * node); + int (*as_bool)(const JSONNODE * node); + JSONNODE * (*as_node)(const JSONNODE * node); + JSONNODE * (*as_array)(const JSONNODE * node); + // #ifdef JSON_BINARY + void * (*as_binary)(const JSONNODE * node, unsigned long * size); + // #endif + // #ifdef JSON_WRITER + char * (*write)(const JSONNODE * node); + char * (*write_formatted)(const JSONNODE * node); + // #endif + + //modifiers + void (*set_name)(JSONNODE * node, const char * name); + // #ifdef JSON_COMMENTS + void (*set_comment)(JSONNODE * node, const char * comment); + // #endif + void (*clear)(JSONNODE * node); + void (*nullify)(JSONNODE * node); + void (*swap)(JSONNODE * node, JSONNODE * node2); + void (*merge)(JSONNODE * node, JSONNODE * node2); + // #ifndef JSON_PREPARSE + void (*preparse)(JSONNODE * node); + // #endif + // #ifdef JSON_BINARY + void (*set_binary)(JSONNODE * node, const void * data, unsigned long length); + // #endif + void (*cast)(JSONNODE * node, char type); + + //children access + void (*reserve)(JSONNODE * node, unsigned int siz); + JSONNODE * (*at)(JSONNODE * node, unsigned int pos); + JSONNODE * (*get)(JSONNODE * node, const char * name); + // #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNODE * (*get_nocase)(JSONNODE * node, const char * name); + JSONNODE * (*pop_back_nocase)(JSONNODE * node, const char * name); + // #endif + void (*push_back)(JSONNODE * node, JSONNODE * node2); + JSONNODE * (*pop_back_at)(JSONNODE * node, unsigned int pos); + JSONNODE * (*pop_back)(JSONNODE * node, const char * name); + // #ifdef JSON_ITERATORS + JSONNODE_ITERATOR (*find)(JSONNODE * node, const char * name); + // #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNODE_ITERATOR (*find_nocase)(JSONNODE * node, const char * name); + // #endif + JSONNODE_ITERATOR (*erase)(JSONNODE * node, JSONNODE_ITERATOR it); + JSONNODE_ITERATOR (*erase_multi)(JSONNODE * node, JSONNODE_ITERATOR start, JSONNODE_ITERATOR end); + JSONNODE_ITERATOR (*insert)(JSONNODE * node, JSONNODE_ITERATOR it, JSONNODE * node2); + JSONNODE_ITERATOR (*insert_multi)(JSONNODE * node, JSONNODE_ITERATOR it, JSONNODE_ITERATOR start, JSONNODE_ITERATOR end); + + //iterator functions + JSONNODE_ITERATOR (*begin)(JSONNODE * node); + JSONNODE_ITERATOR (*end)(JSONNODE * node); + // #endif + + //comparison + int (*equal)(JSONNODE * node, JSONNODE * node2); +} JSONSERVICEINTERFACE, *PJSONSERVICEINTERFACE, *LPJSONSERVICEINTERFACE;
+
+
+#ifndef _STATIC
+extern JSONSERVICEINTERFACE jsi;
+
+#ifndef LIBJSON_H
+#define json_free(a) jsi.free(a) +#define json_delete(a) jsi.delete_(a) +// #ifdef JSON_MEMORY_MANAGE +#define json_free_all() jsi.free_all() +#define json_delete_all() jsi.delete_all() +// #endif +#define json_parse(a) jsi.parse(a) +#define json_strip_white_space(a) jsi.strip_white_space(a) +// #ifdef JSON_VALIDATE +#define json_validate(a) jsi.validate(a) +// #endif +#define json_new_a(a,b) jsi.new_a(a,b) +#define json_new_i(a,b) jsi.new_i(a,b) +#define json_new_f(a,b) jsi.new_f(a,b) +#define json_new_b(a,b) jsi.new_b(a,b) +#define json_new(a) jsi.new_(a) +#define json_copy(a) jsi.copy(a) +#define json_duplicate(a) jsi.duplicate(a) +#define json_set_a(a,b) jsi.set_a(a,b) +#define json_set_i(a,b) jsi.set_i(a,b) +#define json_set_f(a,b) jsi.set_f(a,b) +#define json_set_b(a,b) jsi.set_b(a,b) +#define json_set_n(a,b) jsi.set_n(a,b) +#define json_type(a) jsi.type(a) +#define json_size(a) jsi.size(a) +#define json_empty(a) jsi.empty(a) +#define json_name(a) jsi.name(a) +// #ifdef JSON_COMMENTS +#define json_get_comment(a) jsi.get_comment(a) +// #endif +#define json_as_string(a) jsi.as_string(a) +#define json_as_int(a) jsi.as_int(a) +#define json_as_float(a) jsi.as_float(a) +#define json_as_bool(a) jsi.as_bool(a) +#define json_as_node(a) jsi.as_node(a) +#define json_as_array(a) jsi.as_array(a) +// #ifdef JSON_BINARY +#define json_as_binary(a,b) jsi.as_binary(a,b) +// #endif +// #ifdef JSON_WRITER +#define json_write(a) jsi.write(a) +#define json_write_formatted(a) jsi.write_formatted(a) +// #endif +#define json_set_name(a,b) jsi.set_name(a,b) +// #ifdef JSON_COMMENTS +#define json_set_comment(a,b) jsi.set_comment(a,b) +// #endif +#define json_clear(a) jsi.clear(a) +#define json_nullify(a) jsi.nullify(a) +#define json_swap(a,b) jsi.swap(a,b) +#define json_merge(a,b) jsi.merge(a,b) +// #ifndef JSON_PREPARSE +#define json_preparse(a) jsi.preparse(a) +// #endif +// #ifdef JSON_BINARY +#define json_set_binary(a,b,c) jsi.set_binary(a,b,c) +// #endif +#define json_cast(a,b) jsi.cast(a,b) +#define json_reserve(a,b) jsi.reserve(a,b) +#define json_at(a,b) jsi.at(a,b) +#define json_get(a,b) jsi.get(a,b) +// #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS +#define json_get_nocase(a,b) jsi.get_nocase(a,b) +#define json_pop_back_nocase(a,b) jsi.pop_back_nocase(a,b) +// #endif +#define json_push_back(a,b) jsi.push_back(a,b) +#define json_pop_back_at(a,b) jsi.pop_back_at(a,b) +#define json_pop_back(a,b) jsi.pop_back(a,b) +// #ifdef JSON_ITERATORS +#define json_find(a,b) jsi.find(a,b) +// #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS +#define json_find_nocase(a,b) jsi.find_nocase(a,b) +// #endif +#define json_erase(a,b) jsi.erase(a,b) +#define json_erase_multi(a,b,c) jsi.erase_multi(a,b,c) +#define json_insert(a,b,c) jsi.insert(a,b,c) +#define json_insert_multi(a,b,c,d) jsi.insert_multi(a,b,c,d) +#define json_begin(a) jsi.begin(a) +#define json_end(a) jsi.end(a) +#define json_equal(a,b) jsi.equal(a,b) +#endif
+#endif // _STATIC
+
+#define MS_JSON_GETINTERFACE "JSON/GetInterface"
+
+#endif // MIM_LIBJSON_H
diff --git a/src/miranda32_10.vcxproj b/src/miranda32_10.vcxproj index ce87c1a7d8..a549b914dd 100644 --- a/src/miranda32_10.vcxproj +++ b/src/miranda32_10.vcxproj @@ -82,7 +82,7 @@ <PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>commonheaders.h</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
- <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4996;4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FloatingPointModel>Fast</FloatingPointModel>
<IntrinsicFunctions>true</IntrinsicFunctions>
@@ -118,7 +118,7 @@ <PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>commonheaders.h</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
- <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4996;4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FloatingPointModel>Fast</FloatingPointModel>
<IntrinsicFunctions>true</IntrinsicFunctions>
@@ -152,7 +152,7 @@ <PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>commonheaders.h</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
- <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4996;4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -181,7 +181,7 @@ <PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>commonheaders.h</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
- <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4996;4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -244,6 +244,17 @@ <ClInclude Include="modules\database\dblists.h" />
<ClInclude Include="modules\database\profilemanager.h" />
<ClInclude Include="modules\findadd\findadd.h" />
+ <ClInclude Include="modules\json\internalJSONNode.h" />
+ <ClInclude Include="modules\json\JSONChildren.h" />
+ <ClInclude Include="modules\json\JSONDebug.h" />
+ <ClInclude Include="modules\json\JSONDefs.h" />
+ <ClInclude Include="modules\json\JSONMemory.h" />
+ <ClInclude Include="modules\json\JSONNode.h" />
+ <ClInclude Include="modules\json\JSONOptions.h" />
+ <ClInclude Include="modules\json\JSONWorker.h" />
+ <ClInclude Include="modules\json\JSON_Base64.h" />
+ <ClInclude Include="modules\json\libJSON.h" />
+ <ClInclude Include="modules\json\NumberToString.h" />
<ClInclude Include="modules\netlib\netlib.h" />
<ClInclude Include="modules\options\filter.h" />
<ClInclude Include="..\include\m_protoint.h" />
@@ -317,6 +328,42 @@ <ClCompile Include="modules\ignore\ignore.cpp">
<PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
</ClCompile>
+ <ClCompile Include="modules\json\internalJSONNode.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONChildren.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONDebug.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONIterators.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONMemory.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONNode.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONNode_Mutex.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONWorker.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONWriter.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSON_Base64.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\libJSON.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="modules\json\json.cpp">
+ <PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
+ </ClCompile>
<ClCompile Include="modules\langpack\langpack.cpp">
<PrecompiledHeaderFile>..\..\core\commonheaders.h</PrecompiledHeaderFile>
</ClCompile>
diff --git a/src/miranda32_10.vcxproj.filters b/src/miranda32_10.vcxproj.filters index 82a799129c..c9c2782aa4 100644 --- a/src/miranda32_10.vcxproj.filters +++ b/src/miranda32_10.vcxproj.filters @@ -103,6 +103,9 @@ <Filter Include="Resources">
<UniqueIdentifier>{dcc9b0d7-b335-458b-ac09-6cc6cf55e397}</UniqueIdentifier>
</Filter>
+ <Filter Include="Modules\json">
+ <UniqueIdentifier>{8aad13dd-0e8d-4154-ad6f-913724b5a499}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\m_addcontact.h">
@@ -279,6 +282,39 @@ <ClInclude Include="modules\skin\skin.h">
<Filter>Modules\skin</Filter>
</ClInclude>
+ <ClInclude Include="modules\json\internalJSONNode.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\json\JSON_Base64.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\json\JSONChildren.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\json\JSONDebug.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\json\JSONDefs.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\json\JSONMemory.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\json\JSONNode.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\json\JSONOptions.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\json\JSONWorker.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\json\libJSON.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\json\NumberToString.h">
+ <Filter>Modules\json</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="core\commonheaders.cpp">
@@ -626,6 +662,42 @@ <ClCompile Include="modules\icolib\skin2opts.cpp">
<Filter>Modules\icolib</Filter>
</ClCompile>
+ <ClCompile Include="modules\json\internalJSONNode.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSON_Base64.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONChildren.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONDebug.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONIterators.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONMemory.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONNode.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONNode_Mutex.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONWorker.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\JSONWriter.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\libJSON.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\json\json.cpp">
+ <Filter>Modules\json</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="res\_blank.ico">
diff --git a/src/modules/json/JSONChildren.cpp b/src/modules/json/JSONChildren.cpp new file mode 100644 index 0000000000..371c19880a --- /dev/null +++ b/src/modules/json/JSONChildren.cpp @@ -0,0 +1,98 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+
+#include "JSONChildren.h" +#include "JSONNode.h" + +void jsonChildren::inc(void){ + if (mysize == mycapacity){ //it's full + if (!mycapacity){ //the array hasn't been created yet + JSON_ASSERT(!array, JSON_TEXT("Expanding a 0 capacity array, but not null")); + #ifdef JSON_LESS_MEMORY + array = json_malloc<JSONNode*>(1); + mycapacity = 1; + #else + array = json_malloc<JSONNode*>(8); //8 seems average for JSON, and it's only 64 bytes + mycapacity = 8; + #endif + } else { + #ifdef JSON_LESS_MEMORY + mycapacity += 1; //increment the size of the array + #else + mycapacity <<= 1; //double the size of the array + #endif + array = json_realloc<JSONNode*>(array, mycapacity); + } + } +} + + +void jsonChildren::inc(json_index_t amount){ + if (!amount) return; + if (mysize + amount >= mycapacity){ //it's full + if (!mycapacity){ //the array hasn't been created yet + JSON_ASSERT(!array, JSON_TEXT("Expanding a 0 capacity array, but not null")); + #ifdef JSON_LESS_MEMORY + array = json_malloc<JSONNode*>(amount); + mycapacity = amount; + #else + array = json_malloc<JSONNode*>(amount > 8 ? amount : 8); //8 seems average for JSON, and it's only 64 bytes + mycapacity = amount > 8 ? amount : 8; + #endif + } else { + #ifdef JSON_LESS_MEMORY + mycapacity = mysize + amount; //increment the size of the array + #else + while(mysize + amount > mycapacity){ + mycapacity <<= 1; //double the size of the array + } + #endif + array = json_realloc<JSONNode*>(array, mycapacity); + } + } +} + +//actually deletes everything within the vector, this is safe to do on an empty or even a null array +void jsonChildren::deleteAll(void){ + json_foreach((*this), runner){ + JSON_ASSERT(*runner, JSON_TEXT("a null pointer within the children")); + JSONNode::deleteJSONNode(*runner); //this is why I can't do forward declaration + } +} + +void jsonChildren::doerase(JSONNode ** position, json_index_t number){ + JSON_ASSERT(array, JSON_TEXT("erasing something from a null array 2")); + JSON_ASSERT(position >= array, JSON_TEXT("position is beneath the start of the array 2")); + JSON_ASSERT(position + number <= array + mysize, JSON_TEXT("erasing out of bounds 2")); + if (position + number >= array + mysize){ + mysize = (json_index_t)(position - array); + #ifndef JSON_ISO_STRICT + JSON_ASSERT((long long)position - (long long)array >= 0, JSON_TEXT("doing negative allocation")); + #endif + } else { + memmove(position, position + number, (mysize - (position - array) - number) * sizeof(JSONNode *)); + mysize -= number; + } +} diff --git a/src/modules/json/JSONChildren.h b/src/modules/json/JSONChildren.h new file mode 100644 index 0000000000..d7a38bb9dc --- /dev/null +++ b/src/modules/json/JSONChildren.h @@ -0,0 +1,234 @@ +#ifndef JSONCHILDREN_H +#define JSONCHILDREN_H + +#include "JSONMemory.h" +#include "JSONDebug.h" //for JSON_ASSERT macro + +#define json_foreach(children, iterator)\ + JSONNode ** iterator = children.begin();\ + for(JSONNode ** iterator##_end = children.end(); iterator != iterator##_end; ++iterator) + +/* + This class is essentially a vector that has been heavily optimized for the specific purpose + of holding JSONNode children. It acts the same way as a vector, it has a automatically + expanding array. On destruction, this container automatically destroys everything contained + in it as well, so that you libJSON doesn't have to do that. + + T is JSONNode*, I can't define it that way directly because JSONNode uses this container, and because + the container deletes the children automatically, forward declaration can't be used + */ + +class JSONNode; //forward declaration + +class jsonChildren { +public: + //starts completely empty and the array is not allocated + jsonChildren(void) : array(0), mysize(0), mycapacity(0) { } + + //deletes the array and everything that is contained within it (using delete) + ~jsonChildren(void){ + if (array){ //the following function calls are safe, but take more time than a check here + deleteAll(); + libjson_free<JSONNode*>(array); + } + } + + //increase the size of the array + void inc(json_index_t amount); + void inc(void); + + //Adds something to the vector, doubling the array if necessary + void push_back(JSONNode * item){ + inc(); + array[mysize++] = item; + } + + //Adds something to the front of the vector, doubling the array if necessary + void push_front(JSONNode * item){ + inc(); + memmove(array + 1, array, mysize++ * sizeof(JSONNode *)); + array[0] = item; + } + + //gets an item out of the vector by it's position + inline JSONNode * operator[] (json_index_t position) const { + JSON_ASSERT(position < mysize, JSON_TEXT("Using [] out of bounds")); + JSON_ASSERT(position < mycapacity, JSON_TEXT("Using [] out of bounds")); + JSON_ASSERT(array, JSON_TEXT("Array is null")); + return array[position]; + } + + //returns the allocated capacity, but keep in mind that some might not be valid + inline json_index_t capacity() const { + return mycapacity; + } + + //returns the number of valid objects within the vector + inline json_index_t size() const { + return mysize; + } + + //tests whether or not the vector is empty + inline bool empty() const { + return mysize == 0; + } + + //clears (and deletes) everything from the vector and sets it's size to 0 + inline void clear() { + if (array){ //don't bother clearing anything if there is nothing in it + JSON_ASSERT(mycapacity != 0, JSON_TEXT("mycapacity is not zero, but array is null")); + deleteAll(); + mysize = 0; + } + JSON_ASSERT(mysize == 0, JSON_TEXT("mysize is not zero after clear")); + } + + //returns the beginning of the array + inline JSONNode ** begin(void) const { + return array; + } + + //returns the end of the array + inline JSONNode ** end(void) const { + return array + mysize; + } + + //makes sure that even after shirnking and expanding, the iterator is in same relative position + struct iteratorKeeper { + public: + #ifdef JSON_LIBRARY + iteratorKeeper(jsonChildren * pthis, JSONNode ** & position) : + myRelativeOffset((json_index_t)(position - pthis -> array)), + #else + iteratorKeeper(jsonChildren * pthis, JSONNode ** & position, bool reverse = false) : + myRelativeOffset(reverse ? (json_index_t)(pthis -> array + (size_t)pthis -> mysize - position) : (json_index_t)(position - pthis -> array)), + myReverse(reverse), + #endif + myChildren(pthis), + myPos(position){} + + ~iteratorKeeper(void){ + #ifdef JSON_LIBRARY + myPos = myChildren -> array + myRelativeOffset; + #else + if (myReverse){ + myPos = myChildren -> array + myChildren -> mysize - myRelativeOffset; + } else { + myPos = myChildren -> array + myRelativeOffset; + } + #endif + } + private: + iteratorKeeper(const iteratorKeeper &); + iteratorKeeper & operator = (const iteratorKeeper &); + + jsonChildren * myChildren; + JSONNode ** & myPos; + json_index_t myRelativeOffset; + #ifndef JSON_LIBRARY + bool myReverse BITS(1); + #endif + }; + + //This function DOES NOT delete the item it points to + inline void erase(JSONNode ** & position){ + JSON_ASSERT(array, JSON_TEXT("erasing something from a null array 1")); + JSON_ASSERT(position >= array, JSON_TEXT("position is beneath the start of the array 1")); + JSON_ASSERT(position <= array + mysize, JSON_TEXT("erasing out of bounds 1")); + memmove(position, position + 1, (mysize-- - (position - array) - 1) * sizeof(JSONNode *)); + iteratorKeeper ik(this, position); + shrink(); + } + + //This function DOES NOT delete the item it points to + inline void erase(JSONNode ** & position, json_index_t number){ + doerase(position, number); + iteratorKeeper ik(this, position); + shrink(); + } + + + //This function DOES NOT delete the item it points to + inline void erase(JSONNode ** position, json_index_t number, JSONNode ** & starter){ + doerase(position, number); + iteratorKeeper ik(this, starter); + shrink(); + } + + #ifdef JSON_LIBRARY + void insert(JSONNode ** & position, JSONNode * item){ + #else + void insert(JSONNode ** & position, JSONNode * item, bool reverse = false){ + #endif + //position isnt relative to array because of realloc + JSON_ASSERT(position >= array, JSON_TEXT("position is beneath the start of the array insert 1")); + JSON_ASSERT(position <= array + mysize, JSON_TEXT("position is above the end of the array insert 1")); + { + #ifdef JSON_LIBRARY + iteratorKeeper ik(this, position); + #else + iteratorKeeper ik(this, position, reverse); + #endif + inc(); + } + memmove(position + 1, position, (mysize++ - (position - array)) * sizeof(JSONNode *)); + *position = item; + } + + void insert(JSONNode ** & position, JSONNode ** items, json_index_t num){ + JSON_ASSERT(position >= array, JSON_TEXT("position is beneath the start of the array insert 2")); + JSON_ASSERT(position <= array + mysize, JSON_TEXT("position is above the end of the array insert 2")); + { + iteratorKeeper ik(this, position); + inc(num); + } + const size_t ptrs = ((JSONNode **)(array + mysize)) - position; + memmove(position + num, position, ptrs * sizeof(JSONNode *)); + memcpy(position, items, num * sizeof(JSONNode *)); + mysize += num; + } + + inline void reserve(json_index_t amount){ + JSON_ASSERT(!array, JSON_TEXT("reserve is not meant to expand a preexisting array")); + JSON_ASSERT(!mycapacity, JSON_TEXT("reservec is not meant to expand a preexisting array")); + JSON_ASSERT(!mysize, JSON_TEXT("reserves is not meant to expand a preexisting array")); + array = json_malloc<JSONNode*>(mycapacity = amount); + } + + inline void reserve2(json_index_t amount){ + if (array){ + if (mycapacity < amount) inc(amount - mycapacity); + } else { + reserve(amount); + } + } + + //shrinks the array to only as large as it needs to be to hold everything within it + inline void shrink() { + if (mysize == 0){ //size is zero, we should completely free the array + libjson_free<JSONNode*>(array); //free does checks for a null pointer, so don't bother checking + array = 0; + #ifdef JSON_LESS_MEMORY + } else { //need to shrink it, using realloc + JSON_ASSERT(array, JSON_TEXT("shrinking a null array that is not size 0")); + array = json_realloc<JSONNode*>(array, mysize); + #endif + } + mycapacity = mysize; + } +JSON_PRIVATE + //to make sure it's not copyable + jsonChildren(const jsonChildren &); + jsonChildren & operator = (const jsonChildren &); + + void deleteAll(void); //implemented in JSONNode.cpp + void doerase(JSONNode ** position, json_index_t number); + + JSONNode ** array; //the expandable array + + json_index_t mysize; //the number of valid items + json_index_t mycapacity; //the number of possible items +}; + + +#endif diff --git a/src/modules/json/JSONDebug.cpp b/src/modules/json/JSONDebug.cpp new file mode 100644 index 0000000000..7e13c11a85 --- /dev/null +++ b/src/modules/json/JSONDebug.cpp @@ -0,0 +1,66 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+
+#include "libJSON.h"
+#include "m_json.h"
+ +#ifdef JSON_DEBUG + +#ifdef JSON_STDERROR + #include <iostream> //need std::cerr +#else + //otherwise, use a callback to tell the end user what happened + json_error_callback_t ErrorCallback = 0; + void JSONDebug::register_callback(json_error_callback_t callback){ + ErrorCallback = callback; + } +#endif + +//Something went wrong or an assert failed +void JSONDebug::_JSON_FAIL(const json_string & msg){ + #ifdef JSON_STDERROR //no callback, just use stderror + #ifndef JSON_UNICODE + std::cerr << msg << std::endl; + #else + std::cerr << std::string(msg.begin(), msg.end()) << std::endl; + #endif + #else + if (ErrorCallback){ //only do anything if the callback is registered + #ifdef JSON_LIBRARY + ErrorCallback(msg.c_str()); + #else + ErrorCallback(msg); + #endif + } + #endif +} + +//asserts that condition is true, more useful than cassert because it lets you keep going +void JSONDebug::_JSON_ASSERT(bool condition, const json_string & msg){ + if (!condition){ + _JSON_FAIL(msg); + } +} +#endif diff --git a/src/modules/json/JSONDebug.h b/src/modules/json/JSONDebug.h new file mode 100644 index 0000000000..1d4c89fbaa --- /dev/null +++ b/src/modules/json/JSONDebug.h @@ -0,0 +1,68 @@ +#ifndef JSON_DEBUG_H +#define JSON_DEBUG_H + +#include "JSONDefs.h" +#include "JSONOptions.h" + +#ifdef JSON_UNIT_TEST + #define JSON_PRIVATE +#else + #define JSON_PRIVATE private: +#endif + +#ifdef JSON_DEBUG + #ifdef JSON_SAFE + #define JSON_ASSERT_SAFE(condition, msg, code)\ + {\ + if (!(condition)) {\ + JSON_FAIL(msg);\ + code\ + }\ + } + #define JSON_FAIL_SAFE(msg, code)\ + {\ + JSON_FAIL(msg);\ + code\ + } + #else + #define JSON_ASSERT_SAFE(condition, msg, code) JSON_ASSERT(condition, msg) + #define JSON_FAIL_SAFE(msg, code) JSON_FAIL(msg) + #endif + + #define JSON_FAIL JSONDebug::_JSON_FAIL + #define JSON_ASSERT JSONDebug::_JSON_ASSERT + + class JSONDebug { + public: + #ifndef JSON_STDERROR + static void register_callback(json_error_callback_t callback); + #endif + static void _JSON_FAIL(const json_string & msg); + static void _JSON_ASSERT(bool condition, const json_string & msg); + }; +#else + #ifdef JSON_SAFE + #define JSON_ASSERT_SAFE(condition, msg, code)\ + {\ + if (!(condition)) {\ + code\ + }\ + } + #define JSON_FAIL_SAFE(msg, code)\ + {\ + code\ + } + #else + #define JSON_ASSERT_SAFE(condition, msg, code) + #define JSON_FAIL_SAFE(msg, code) + #endif + + #define JSON_ASSERT(condition, msg) + #define JSON_FAIL(msg) +#endif + +static const json_string EMPTY_STRING; +static const std::string EMPTY_STRING2; + +#endif + diff --git a/src/modules/json/JSONDefs.h b/src/modules/json/JSONDefs.h new file mode 100644 index 0000000000..678d059400 --- /dev/null +++ b/src/modules/json/JSONDefs.h @@ -0,0 +1,81 @@ +#ifndef JSONDEFS_H +#define JSONDEFS_H + +/* + Defines all of the types of functions and various other definitions + that are used in C applications, this is very useful if dynamically loading + the library instead of linking. +*/ + +#include "JSONOptions.h" + +#define JSON_NULL '\0' +#define JSON_STRING '\1' +#define JSON_NUMBER '\2' +#define JSON_BOOL '\3' +#define JSON_ARRAY '\4' +#define JSON_NODE '\5' + +#ifdef __cplusplus + #include <string> +#endif + +#ifdef JSON_UNICODE + #ifdef JSON_ISO_STRICT + #error, You can not use unicode under ISO Strict C++ + #endif + #define json_char wchar_t + #ifdef __cplusplus + #include <cwchar> //need wide characters + typedef std::wstring json_string; + #else + #include <wchar.h> //need wide characters + #endif + #define JSON_TEXT(s) L ## s + #define json_strlen wcslen + #define json_strcmp wcscmp +#else + #define json_char char + #ifdef __cplusplus + typedef std::string json_string; + #endif + #define JSON_TEXT(s) s + #define json_strlen strlen + #define json_strcmp strcmp +#endif + +#ifdef JSON_LESS_MEMORY + #define BITS(x) :x //tells the compiler how many bits to use for a field + typedef float json_number; +#else + #define BITS(x) + typedef double json_number; +#endif + +#if defined JSON_DEBUG || defined JSON_SAFE + #ifdef JSON_LIBRARY + typedef void (*json_error_callback_t)(const json_char *); + #else + typedef void (*json_error_callback_t)(const json_string &); + #endif +#endif + +#ifdef JSON_INDEX_TYPE + typedef JSON_INDEX_TYPE json_index_t; +#else + typedef unsigned int json_index_t; +#endif + +typedef void (*json_mutex_callback_t)(void *); +typedef void (*json_free_t)(void *); +#ifndef JSON_LIBRARY + typedef void * (*json_malloc_t)(size_t); + typedef void * (*json_realloc_t)(void *, size_t); +#else + #define JSONNODE void //so that JSONNODE* is void* + typedef JSONNODE** JSONNODE_ITERATOR; + typedef void * (*json_malloc_t)(unsigned long); + typedef void * (*json_realloc_t)(void *, unsigned long); +#endif + +#endif //JSONDEFS_H diff --git a/src/modules/json/JSONIterators.cpp b/src/modules/json/JSONIterators.cpp new file mode 100644 index 0000000000..edb8ff7618 --- /dev/null +++ b/src/modules/json/JSONIterators.cpp @@ -0,0 +1,239 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+
+#include "JSONNode.h" + +#ifdef JSON_ITERATORS + #ifdef JSON_REF_COUNT + #define JSON_ASSERT_UNIQUE(x) JSON_ASSERT(internal -> refcount == 1, json_string(JSON_TEXT(x)) + JSON_TEXT(" in non single reference")) + #else + #define JSON_ASSERT_UNIQUE(x) (void)0 + #endif + + #ifdef JSON_MUTEX_CALLBACKS + #define JSON_MUTEX_COPY2 ,internal -> mylock + #else + #define JSON_MUTEX_COPY2 + #endif + +JSONNode::json_iterator JSONNode::find(const json_string & name_t){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE, JSON_TEXT("finding a non-iteratable node")); + makeUniqueInternal(); + if (JSONNode ** res = internal -> at(name_t)) { + return ptr_to_json_iterator(res); + } + return end(); +} + +#ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNode::json_iterator JSONNode::find_nocase(const json_string & name_t){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE, JSON_TEXT("finding a non-iteratable node")); + makeUniqueInternal(); + if (JSONNode ** res = internal -> at_nocase(name_t)) { + return ptr_to_json_iterator(res); + } + return end(); + } +#endif + +JSONNode::json_iterator JSONNode::erase(json_iterator pos){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("erasing a non-iteratable node")); + JSON_ASSERT_UNIQUE("erase 1"); + JSON_ASSERT_SAFE(pos < end(), JSON_TEXT("erase out of range"), return end();); + JSON_ASSERT_SAFE(pos >= begin(), JSON_TEXT("erase out of range"), return begin();); + deleteJSONNode(*(json_iterator_ptr(pos))); + internal -> Children.erase(json_iterator_ptr(pos)); + return (empty()) ? end() : pos; +} + +JSONNode::json_iterator JSONNode::erase(json_iterator _start, const json_iterator & _end){ + if (_start == _end) return _start; + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("erasing a non-iteratable node")); + JSON_ASSERT_UNIQUE("erase 3"); + JSON_ASSERT_SAFE(_start <= end(), JSON_TEXT("erase out of lo range"), return end();); + JSON_ASSERT_SAFE(_end <= end(), JSON_TEXT("erase out of hi range"), return end();); + JSON_ASSERT_SAFE(_start >= begin(), JSON_TEXT("erase out of lo range"), return begin();); + JSON_ASSERT_SAFE(_end >= begin(), JSON_TEXT("erase out of hi range"), return begin();); + for (JSONNode ** pos = json_iterator_ptr(_start); pos < json_iterator_ptr(_end); ++pos){ + deleteJSONNode(*pos); + } + + internal -> Children.erase(json_iterator_ptr(_start), json_iterator_ptr(_end) - json_iterator_ptr(_start)); + return (empty()) ? end() : _start; +} + +#ifdef JSON_LIBRARY +JSONNode::json_iterator JSONNode::insert(json_iterator pos, JSONNode * x){ +#else +JSONNode::json_iterator JSONNode::insert(json_iterator pos, const JSONNode & x){ +#endif + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("erasing a non-iteratable node")); + JSON_ASSERT_UNIQUE("insert 1"); + if (json_iterator_ptr(pos) >= internal -> Children.end()) { + internal -> push_back(x); + return end() - 1; + } + JSON_ASSERT_SAFE(pos >= begin(), JSON_TEXT("insert out of lo range"), return begin();); + #ifdef JSON_LIBRARY + internal -> Children.insert(json_iterator_ptr(pos), x); + #else + internal -> Children.insert(json_iterator_ptr(pos), newJSONNode(x)); + #endif + return pos; +} + +JSONNode::json_iterator JSONNode::insertFFF(json_iterator pos, JSONNode ** const _start, JSONNode ** const _end){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("erasing a non-iteratable node")); + JSON_ASSERT_UNIQUE("insertFFF"); + JSON_ASSERT_SAFE(pos <= end(), JSON_TEXT("insert out of high range"), return end();); + JSON_ASSERT_SAFE(pos >= begin(), JSON_TEXT("insert out of low range"), return begin();); + const size_t num = _end - _start; + json_auto<JSONNode *> mem(num); + JSONNode ** runner = mem.ptr; + for (JSONNode ** po = _start; po < _end; ++po){ + *runner++ = newJSONNode(*(*po) JSON_MUTEX_COPY2); + } + internal -> Children.insert(json_iterator_ptr(pos), mem.ptr, num); + return pos; +} + +#ifndef JSON_LIBRARY + JSONNode::const_iterator JSONNode::find(const json_string & name_t) const { + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE, JSON_TEXT("finding a non-iteratable node")); + if (JSONNode ** res = internal -> at(name_t)) { + return JSONNode::const_iterator(res); + } + return JSONNode::const_iterator(internal -> end()); + } + + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNode::const_iterator JSONNode::find_nocase(const json_string & name_t) const { + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE, JSON_TEXT("finding a non-iteratable node")); + if (JSONNode ** res = internal -> at_nocase(name_t)) { + return JSONNode::const_iterator(res); + } + return JSONNode::const_iterator(internal -> end()); + } + #endif + + JSONNode::reverse_iterator JSONNode::erase(reverse_iterator pos){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("erasing a non-iteratable node")); + JSON_ASSERT_UNIQUE("erase 2"); + JSON_ASSERT_SAFE(pos < rend(), JSON_TEXT("erase out of range"), return rend();); + JSON_ASSERT_SAFE(pos >= rbegin(), JSON_TEXT("erase out of range"), return rbegin();); + deleteJSONNode(*(pos.it)); + internal -> Children.erase(pos.it); + return (empty()) ? rend() : pos + 1; + } + + JSONNode::reverse_iterator JSONNode::erase(reverse_iterator _start, const reverse_iterator & _end){ + if (_start == _end) return _start; + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("erasing a non-iteratable node")); + JSON_ASSERT_UNIQUE("erase 4"); + JSON_ASSERT_SAFE(_start <= rend(), JSON_TEXT("erase out of lo range"), return rend();); + JSON_ASSERT_SAFE(_end <= rend(), JSON_TEXT("erase out of hi range"), return rend();); + JSON_ASSERT_SAFE(_start >= rbegin(), JSON_TEXT("erase out of lo range"), return rbegin();); + JSON_ASSERT_SAFE(_end >= rbegin(), JSON_TEXT("erase out of hi range"), return rbegin();); + for (JSONNode ** pos = _start.it; pos > _end.it; --pos){ + deleteJSONNode(*pos); + } + const size_t num = _start.it - _end.it; + internal -> Children.erase(_end.it + 1, num, _start.it); + return (empty()) ? rend() : _start + num; + } + + JSONNode::reverse_iterator JSONNode::insert(reverse_iterator pos, const JSONNode & x){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("erasing a non-iteratable node")); + JSON_ASSERT_UNIQUE("insert 1"); + if (pos.it < internal -> Children.begin()) { + internal -> push_front(x); + return rend() - 1; + } + JSON_ASSERT_SAFE(pos >= rbegin(), JSON_TEXT("insert out of range"), return rbegin();); + internal -> Children.insert(++pos.it, newJSONNode(x), true); + return pos; + } + + JSONNode::reverse_iterator JSONNode::insertRFF(reverse_iterator pos, JSONNode ** const _start, JSONNode ** const _end){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("erasing a non-iteratable node")); + JSON_ASSERT_UNIQUE("insert RFF"); + JSON_ASSERT_SAFE(pos <= rend(), JSON_TEXT("insert out of range"), return rend();); + JSON_ASSERT_SAFE(pos >= rbegin(), JSON_TEXT("insert out of range"), return rbegin();); + const size_t num = _end - _start; + json_auto<JSONNode *> mem(num); + JSONNode ** runner = mem.ptr + num; + for (JSONNode ** po = _start; po < _end; ++po){ //fill it backwards + *(--runner) = newJSONNode(*(*po) JSON_MUTEX_COPY2); + } + internal -> Children.insert(++pos.it, mem.ptr, num); + return pos - num + 1; + } + + JSONNode::iterator JSONNode::insertFRR(json_iterator pos, JSONNode ** const _start, JSONNode ** const _end){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("erasing a non-iteratable node")); + JSON_ASSERT_UNIQUE("insert FRR"); + JSON_ASSERT_SAFE(pos <= end(), JSON_TEXT("insert out of range"), return end();); + JSON_ASSERT_SAFE(pos >= begin(), JSON_TEXT("insert out of range"), return begin();); + const size_t num = _start - _end; + json_auto<JSONNode *> mem(num); + JSONNode ** runner = mem.ptr; + for (JSONNode ** po = _start; po > _end; --po){ + *runner++ = newJSONNode(*(*po) JSON_MUTEX_COPY2); + } + internal -> Children.insert(pos.it, mem.ptr, num); + return pos; + } + + JSONNode::reverse_iterator JSONNode::insertRRR(reverse_iterator pos, JSONNode ** const _start, JSONNode ** const _end){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("erasing a non-iteratable node")); + JSON_ASSERT_UNIQUE("insert RRR"); + JSON_ASSERT_SAFE(pos <= rend(), JSON_TEXT("insert out of range"), return rend();); + JSON_ASSERT_SAFE(pos >= rbegin(), JSON_TEXT("insert out of range"), return rbegin();); + const size_t num = _start - _end; + json_auto<JSONNode *> mem(num); + JSONNode ** runner = mem.ptr; + for (JSONNode ** po = _start; po > _end; --po){ + *runner++ = newJSONNode(*(*po) JSON_MUTEX_COPY2); + } + internal -> Children.insert(++pos.it, mem.ptr, num); + return pos - num + 1; + } +#endif + +#endif diff --git a/src/modules/json/JSONMemory.cpp b/src/modules/json/JSONMemory.cpp new file mode 100644 index 0000000000..bd9cd045a2 --- /dev/null +++ b/src/modules/json/JSONMemory.cpp @@ -0,0 +1,106 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+
+#include "JSONMemory.h" +#include "JSONNode.h" + +#ifdef JSON_MEMORY_MANAGE + void auto_expand::purge(void){ + for(std::map<void *, void *>::iterator i = mymap.begin(), en = mymap.end(); i != en; ++i){ + #if defined(JSON_DEBUG) || defined(JSON_SAFE) + void * temp = (void*)i -> first; //because its pass by reference + libjson_free<void>(temp); + #else + libjson_free<void>((void*)i -> first); + #endif + } + } + + void auto_expand_node::purge(void){ + for(std::map<void *, JSONNode *>::iterator i = mymap.begin(), en = mymap.end(); i != en; ++i){ + JSONNode::deleteJSONNode((JSONNode *)i -> second); + } + } +#endif + +#ifdef JSON_MEMORY_CALLBACKS + +json_malloc_t mymalloc = 0; +json_realloc_t myrealloc = 0; +json_free_t myfree = 0; + +void * JSONMemory::json_malloc(size_t siz){ + if (mymalloc){ + #ifdef JSON_DEBUG //in debug mode, see if the malloc was successful + void * result = mymalloc(siz); + JSON_ASSERT(result, JSON_TEXT("out of memory")); + return result; + #else + return mymalloc(siz); + #endif + } + #ifdef JSON_DEBUG //in debug mode, see if the malloc was successful + void * result = malloc(siz); + JSON_ASSERT(result, JSON_TEXT("out of memory")); + return result; + #else + return malloc(siz); + #endif +} + +void * JSONMemory::json_realloc(void * ptr, size_t siz){ + if (myrealloc){ + #ifdef JSON_DEBUG //in debug mode, see if the malloc was successful + void * result = myrealloc(ptr, siz); + JSON_ASSERT(result, JSON_TEXT("out of memory")); + return result; + #else + return myrealloc(ptr, siz); + #endif + } + #ifdef JSON_DEBUG //in debug mode, see if the malloc was successful + void * result = realloc(ptr, siz); + JSON_ASSERT(result, JSON_TEXT("out of memory")); + return result; + #else + return realloc(ptr, siz); + #endif +} + +void JSONMemory::json_free(void * ptr){ + if (myfree){ + myfree(ptr); + } else { + free(ptr); + } +} + +void JSONMemory::registerMemoryCallbacks(json_malloc_t mal, json_realloc_t real, json_free_t fre){ + mymalloc = mal; + myrealloc = real; + myfree = fre; +} + +#endif diff --git a/src/modules/json/JSONMemory.h b/src/modules/json/JSONMemory.h new file mode 100644 index 0000000000..c04c45eff5 --- /dev/null +++ b/src/modules/json/JSONMemory.h @@ -0,0 +1,134 @@ +#ifndef JSON_MEMORY_H +#define JSON_MEMORY_H + +#include <cstdlib> //for malloc, realloc, and free +#include <cstring> //for memmove +#include "JSONOptions.h" +#include "JSONDebug.h" + +#if defined(JSON_DEBUG) || defined(JSON_SAFE) + #define JSON_FREE_PASSTYPE & +#else + #define JSON_FREE_PASSTYPE +#endif + +#ifdef JSON_MEMORY_CALLBACKS + class JSONMemory { + public: + static void * json_malloc(size_t siz); + static void * json_realloc(void * ptr, size_t siz); + static void json_free(void * ptr); + static void registerMemoryCallbacks(json_malloc_t mal, json_realloc_t real, json_free_t fre); + }; + + template <typename T> static inline T * json_malloc(size_t count){ + return (T *)JSONMemory::json_malloc(sizeof(T) * count); + } + + template <typename T> static inline T * json_realloc(T * ptr, size_t count){ + return (T *)JSONMemory::json_realloc(ptr, sizeof(T) * count); + } + + template <typename T> static inline void libjson_free(T * JSON_FREE_PASSTYPE ptr){ + JSONMemory::json_free(ptr); + #if defined(JSON_DEBUG) || defined(JSON_SAFE) //in debug or safe mode, set the pointer to 0 so that it can't be used again + ptr = 0; + #endif + } +#else + template <typename T> + static inline T * json_malloc(size_t count){ + #ifdef JSON_DEBUG //in debug mode, see if the malloc was successful + void * result = malloc(count * sizeof(T)); + JSON_ASSERT(result, JSON_TEXT("out of memory")); + #ifdef JSON_NULL_MEMORY + memset(result, '\0', count * sizeof(T)); + #endif + return (T *)result; + #else + return (T *)malloc(count * sizeof(T)); + #endif + } + + template <typename T> + static inline void libjson_free(T * JSON_FREE_PASSTYPE ptr){ + free(ptr); + #if defined(JSON_DEBUG) || defined(JSON_SAFE) //in debug or safe mode, set the pointer to 0 so that it can't be used again + ptr = 0; + #endif + } + + template <typename T> + static inline T * json_realloc(T * ptr, size_t count){ + #ifdef JSON_DEBUG //in debug mode, check the results of realloc to be sure it was successful + void * result = realloc(ptr, count * sizeof(T)); + JSON_ASSERT(result, JSON_TEXT("out of memory")); + #ifdef JSON_NULL_MEMORY + memset(result, '\0', count * sizeof(T)); + #endif + return (T *)result; + #else + return (T *)realloc(ptr, count * sizeof(T)); + #endif + } +#endif + +#ifdef JSON_MEMORY_MANAGE + #include <map> + class JSONNode; + struct auto_expand { + auto_expand(void) : mymap() {} + ~auto_expand(void){ purge(); } + void purge(void); + inline void clear(void){ purge(); mymap.clear(); } + inline void * insert(void * ptr){ mymap[ptr] = ptr; return ptr; } + inline void remove(void * ptr){ + std::map<void *, void *>::iterator i = mymap.find(ptr); + JSON_ASSERT(i != mymap.end(), JSON_TEXT("Removing a non-managed item")); + mymap.erase(i); + } + std::map<void *, void *> mymap; + }; + + struct auto_expand_node { + auto_expand_node(void) : mymap() {} + ~auto_expand_node(void){ purge(); } + void purge(void); + inline void clear(void){ purge(); mymap.clear(); } + inline JSONNode * insert(JSONNode * ptr){ mymap[ptr] = ptr; return ptr; } + inline void remove(void * ptr){ + std::map<void *, JSONNode *>::iterator i = mymap.find(ptr); + if(i != mymap.end()) mymap.erase(i); + } + std::map<void *, JSONNode *> mymap; + }; +#endif + +//The C++ way, use an self-deleting pointer and let the optimizer decide when it gets destroyed +template <typename T> +class json_auto { + public: + json_auto(void) : ptr(0){} + json_auto(size_t count) : ptr(json_malloc<T>(count)) {} + ~json_auto(void){ + libjson_free<T>(ptr); + } + void set(T * p){ + ptr = p; + } + T * ptr; + private: + json_auto(const json_auto &); + json_auto & operator =(const json_auto &); +}; + +//Clears a string, if required, frees the memory +static inline void clearString(json_string & str){ + #ifdef JSON_LESS_MEMORY + json_string().swap(str); + #else + str.clear(); + #endif +} + +#endif diff --git a/src/modules/json/JSONNode.cpp b/src/modules/json/JSONNode.cpp new file mode 100644 index 0000000000..8d2a046f69 --- /dev/null +++ b/src/modules/json/JSONNode.cpp @@ -0,0 +1,311 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+
+#include "JSONNode.h" + +#ifdef JSON_UNIT_TEST + int allocCount = 0; + int deallocCount = 0; + int internalAllocCount = 0; + int internalDeallocCount = 0; + int JSONNode::getNodeAllocationCount(void){ return allocCount; } + int JSONNode::getNodeDeallocationCount(void){ return deallocCount; } + int JSONNode::getInternalAllocationCount(void){ return internalAllocCount; } + int JSONNode::getInternalDeallocationCount(void){ return internalDeallocCount; } + void JSONNode::incAllocCount(void){ ++allocCount; } + void JSONNode::decAllocCount(void){ ++deallocCount; } + void JSONNode::incinternalAllocCount(void){ ++internalAllocCount; } + void JSONNode::decinternalAllocCount(void){ ++internalDeallocCount; } +#endif + +#define IMPLEMENT_CTOR(type)\ + JSONNode::JSONNode(const json_string & name_t, type value_t) : internal(internalJSONNode::newInternal()) {\ + internal -> Set(value_t);\ + internal -> setname(name_t);\ + incAllocCount();\ + } +IMPLEMENT_FOR_ALL_TYPES(IMPLEMENT_CTOR) + +#ifndef JSON_LIBRARY + JSONNode::JSONNode(const json_string & name_t, const json_char * value_t) : internal(internalJSONNode::newInternal()) { + internal -> Set(json_string(value_t)); + internal -> setname(name_t); + incAllocCount(); + } +#endif + +JSONNode JSONNode::as_node(void) const { + JSON_CHECK_INTERNAL(); + if (type() == JSON_NODE){ + return *this; + } else if (type() == JSON_ARRAY){ + JSONNode res = duplicate(); + res.internal -> _type = JSON_NODE; + return res; + } + #ifdef JSON_MUTEX_CALLBACKS + if (internal -> mylock){ + JSONNode res = JSONNode(JSON_NODE); + res.set_mutex(internal -> mylock); + return res; + } + #endif + return JSONNode(JSON_NODE); +} + +JSONNode JSONNode::as_array(void) const { + JSON_CHECK_INTERNAL(); + if (type() == JSON_ARRAY){ + return *this; + } else if (type() == JSON_NODE){ + JSONNode res = duplicate(); + res.internal -> _type = JSON_ARRAY; + json_foreach(res.internal -> Children, runner){ + (*runner) -> set_name(JSON_TEXT("")); + } + return res; + } + #ifdef JSON_MUTEX_CALLBACKS + if (internal -> mylock){ + JSONNode res = JSONNode(JSON_ARRAY); + res.set_mutex(internal -> mylock); + return res; + } + #endif + return JSONNode(JSON_ARRAY); +} + +void JSONNode::cast(char newtype){ + JSON_CHECK_INTERNAL(); + if (newtype == type()) return; + + switch(newtype){ + case JSON_NULL: + nullify(); + return; + case JSON_STRING: + *this = as_string(); + return; + case JSON_NUMBER: + *this = as_float(); + return; + case JSON_BOOL: + *this = as_bool(); + return; + case JSON_ARRAY: + *this = as_array(); + return; + case JSON_NODE: + *this = as_node(); + return; + } + JSON_FAIL(JSON_TEXT("cast to unknown type")); +} + +//different just to supress the warning +#ifdef JSON_REF_COUNT +void JSONNode::merge(JSONNode & other){ +#else +void JSONNode::merge(JSONNode &) { +#endif + JSON_CHECK_INTERNAL(); + #ifdef JSON_REF_COUNT + if (internal == other.internal) return; + JSON_ASSERT(*this == other, JSON_TEXT("merging two nodes that aren't equal")); + if (internal -> refcount < other.internal -> refcount){ + *this = other; + } else { + other = *this; + } + #endif +} + +#ifdef JSON_REF_COUNT + void JSONNode::merge(JSONNode * other){ + JSON_CHECK_INTERNAL(); + if (internal == other -> internal) return; + *other = *this; + } + + //different just to supress the warning + void JSONNode::merge(unsigned int num, ...) { +#else + void JSONNode::merge(unsigned int, ...) { +#endif + JSON_CHECK_INTERNAL(); + #ifdef JSON_REF_COUNT + va_list args; + va_start(args, num); + for(unsigned int i = 0; i < num; ++i){ + merge(va_arg(args, JSONNode*)); + } + va_end(args); + #endif +} + +JSONNode JSONNode::duplicate(void) const { + JSON_CHECK_INTERNAL(); + JSONNode mycopy(*this); + #ifdef JSON_REF_COUNT + JSON_ASSERT(internal == mycopy.internal, JSON_TEXT("copy ctor failed to ref count correctly")); + mycopy.makeUniqueInternal(); + #endif + JSON_ASSERT(internal != mycopy.internal, JSON_TEXT("makeUniqueInternal failed")); + return mycopy; +} + +JSONNode & JSONNode::at(json_index_t pos){ + JSON_CHECK_INTERNAL(); + if (pos >= internal -> size()) { + JSON_FAIL(JSON_TEXT("at() out of bounds")); + throw std::out_of_range(EMPTY_STRING2); + } + return (*this)[pos]; +} + +const JSONNode & JSONNode::at(json_index_t pos) const { + JSON_CHECK_INTERNAL(); + if (pos >= internal -> size()) { + JSON_FAIL(JSON_TEXT("at() const out of bounds")); + throw std::out_of_range(EMPTY_STRING2); + } + return (*this)[pos]; +} + +JSONNode & JSONNode::operator[](json_index_t pos){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(pos < internal -> size(), JSON_TEXT("[] out of bounds")); + makeUniqueInternal(); + return *(internal -> at(pos)); +} + +const JSONNode & JSONNode::operator[](json_index_t pos) const { + JSON_CHECK_INTERNAL(); + JSON_ASSERT(pos < internal -> size(), JSON_TEXT("[] const out of bounds")); + return *(internal -> at(pos)); +} + +JSONNode & JSONNode::at(const json_string & name_t){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE, JSON_TEXT("at a non-iteratable node")); + makeUniqueInternal(); + if (JSONNode ** res = internal -> at(name_t)) { + return *(*res); + } + JSON_FAIL(json_string(JSON_TEXT("at could not find child by name: ")) + name_t); + throw std::out_of_range(EMPTY_STRING2); +} + +const JSONNode & JSONNode::at(const json_string & name_t) const { + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE, JSON_TEXT("at a non-iteratable node")); + if (JSONNode ** res = internal -> at(name_t)) { + return *(*res); + } + JSON_FAIL(json_string(JSON_TEXT("at const could not find child by name: ")) + name_t); + throw std::out_of_range(EMPTY_STRING2); +} + +#ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNode & JSONNode::at_nocase(const json_string & name_t){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE, JSON_TEXT("at a non-iteratable node")); + makeUniqueInternal(); + if (JSONNode ** res = internal -> at_nocase(name_t)) { + return *(*res); + } + JSON_FAIL(json_string(JSON_TEXT("at_nocase could not find child by name: ")) + name_t); + throw std::out_of_range(EMPTY_STRING2); + } + + const JSONNode & JSONNode::at_nocase(const json_string & name_t) const { + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE, JSON_TEXT("at a non-iteratable node")); + if (JSONNode ** res = internal -> at_nocase(name_t)) { + return *(*res); + } + JSON_FAIL(json_string(JSON_TEXT("at_nocase const could not find child by name: ")) + name_t); + throw std::out_of_range(EMPTY_STRING2); + } +#endif + +#ifndef JSON_LIBRARY + struct auto_delete { + public: + auto_delete(JSONNode * node) : mynode(node){}; + ~auto_delete(void){ JSONNode::deleteJSONNode(mynode); }; + JSONNode * mynode; + private: + auto_delete(const auto_delete &); + auto_delete & operator = (const auto_delete &); + }; +#endif + +JSONNode JSON_PTR_LIB JSONNode::pop_back(json_index_t pos){ + JSON_CHECK_INTERNAL(); + if (pos >= internal -> size()) { + JSON_FAIL(JSON_TEXT("pop_back out of bounds")); + throw std::out_of_range(EMPTY_STRING2); + } + makeUniqueInternal(); + #ifdef JSON_LIBRARY + return internal -> pop_back(pos); + #else + auto_delete temp(internal -> pop_back(pos)); + return *temp.mynode; + #endif +} + +JSONNode JSON_PTR_LIB JSONNode::pop_back(const json_string & name_t){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE, JSON_TEXT("popping a non-iteratable node")); + #ifdef JSON_LIBRARY + return internal -> pop_back(name_t); + #else + if (JSONNode * res = internal -> pop_back(name_t)) { + auto_delete temp(res); + return *(temp.mynode); + } + JSON_FAIL(json_string(JSON_TEXT("pop_back const could not find child by name: ")) + name_t); + throw std::out_of_range(EMPTY_STRING2); + #endif +} + +#ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNode JSON_PTR_LIB JSONNode::pop_back_nocase(const json_string & name_t){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE, JSON_TEXT("popping a non-iteratable node")); + #ifdef JSON_LIBRARY + return internal -> pop_back_nocase(name_t); + #else + if (JSONNode * res = internal -> pop_back_nocase(name_t)) { + auto_delete temp(res); + return *(temp.mynode); + } + JSON_FAIL(json_string(JSON_TEXT("pop_back_nocase could not find child by name: ")) + name_t); + throw std::out_of_range(EMPTY_STRING2); + #endif + } +#endif diff --git a/src/modules/json/JSONNode.h b/src/modules/json/JSONNode.h new file mode 100644 index 0000000000..36ebcd6145 --- /dev/null +++ b/src/modules/json/JSONNode.h @@ -0,0 +1,923 @@ +#ifndef JSONNODE_H +#define JSONNODE_H + +#include "JSONDefs.h" //for string type +#include "internalJSONNode.h" //internal structure for json value +#include <stdexcept> +#include <cstdarg> //for the ... parameter + +#ifdef JSON_BINARY + #include "JSON_Base64.h" +#endif + +#ifndef JSON_REF_COUNT + #define makeUniqueInternal() (void)0 +#endif + +#define JSON_CHECK_INTERNAL() JSON_ASSERT(internal, JSON_TEXT("no internal")) + +#ifdef JSON_MUTEX_CALLBACKS + #define JSON_MUTEX_COPY_DECL ,void * parentMutex + #define JSON_MUTEX_COPY_DECL2 ,void * parentMutex = 0 +#else + #define JSON_MUTEX_COPY_DECL + #define JSON_MUTEX_COPY_DECL2 +#endif + +#ifdef JSON_LIBRARY + #define JSON_PTR_LIB * + #define JSON_NEW(x) JSONNode::newJSONNode_Shallow(x) + #define DECLARE_FOR_ALL_TYPES(foo)\ + foo(long);\ + foo(json_number);\ + foo(bool);\ + foo(const json_string &); + + #define DECLARE_FOR_ALL_TYPES_CONST(foo)\ + foo(long) const;\ + foo(json_number) const;\ + foo(bool) const;\ + foo(const json_string &) const;\ + foo(const JSONNode &) const; + + #define IMPLEMENT_FOR_ALL_NUMBERS(foo)\ + foo(long)\ + foo(json_number) + + +#else + #define JSON_PTR_LIB + #define JSON_NEW(x) x + #define DECLARE_FOR_ALL_TYPES(foo)\ + foo(char); foo(unsigned char);\ + foo(short); foo(unsigned short);\ + foo(int); foo(unsigned int);\ + foo(long); foo(unsigned long);\ + foo(float); foo(double);\ + foo(bool);\ + foo(const json_string &);\ + foo(const json_char *); + + #define DECLARE_FOR_ALL_TYPES_CONST(foo)\ + foo(char) const; foo(unsigned char) const;\ + foo(short) const; foo(unsigned short) const;\ + foo(int) const; foo(unsigned int) const;\ + foo(long) const; foo(unsigned long) const;\ + foo(float) const; foo(double) const;\ + foo(bool) const;\ + foo(const json_string &) const;\ + foo(const JSONNode &) const;\ + foo(const json_char *) const; + + #define IMPLEMENT_FOR_ALL_NUMBERS(foo)\ + foo(char) foo(unsigned char)\ + foo(short) foo(unsigned short)\ + foo(int) foo(unsigned int)\ + foo(long) foo(unsigned long)\ + foo(float) foo(double) + +#endif + +#define IMPLEMENT_FOR_ALL_TYPES(foo)\ + IMPLEMENT_FOR_ALL_NUMBERS(foo)\ + foo(const json_string &)\ + foo(bool) + +/* + This class is mostly just a wrapper class around internalJSONNode, this class keeps + the reference count and handles copy on write and such. This class is also responsible + for argument checking and throwing exceptions if needed. +*/ + + +class JSONNode { +public: + explicit JSONNode(char mytype = JSON_NODE); + #define DECLARE_CTOR(type) JSONNode(const json_string & name_t, type value_t) + DECLARE_FOR_ALL_TYPES(DECLARE_CTOR) + + JSONNode(const JSONNode & orig); + ~JSONNode(void); + + json_index_t size(void) const; + bool empty(void) const; + void clear(void); + unsigned char type(void) const; + + json_string name(void) const; + void set_name(const json_string & newname); + #ifdef JSON_COMMENTS + void set_comment(const json_string & comment); + json_string get_comment(void) const; + #endif + #ifndef JSON_PREPARSE + void preparse(void); + #endif + #ifdef JSON_VALIDATE + #ifndef JSON_SAFE + #error JSON_VALIDATE also requires JSON_SAFE + #endif + bool validate(void); + #endif + + json_string as_string(void) const; + long as_int(void) const; + json_number as_float(void) const; + bool as_bool(void) const; + JSONNode as_node(void) const; + JSONNode as_array(void) const; + + #ifdef JSON_BINARY + std::string as_binary(void) const; + void set_binary(const unsigned char * bin, json_index_t bytes); + #endif + + JSONNode & at(json_index_t pos); + const JSONNode & at(json_index_t pos) const; + JSONNode & operator[](json_index_t pos); + const JSONNode & operator[](json_index_t pos) const; + + JSONNode & at(const json_string & name_t); + const JSONNode & at(const json_string & name_t) const; + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNode & at_nocase(const json_string & name_t); + const JSONNode & at_nocase(const json_string & name_t) const; + #endif + JSONNode & operator[](const json_string & name_t); + const JSONNode & operator[](const json_string & name_t) const; + + #ifdef JSON_LIBRARY + void push_back(JSONNode * node); + #else + void push_back(const JSONNode & node); + #endif + void reserve(json_index_t size); + JSONNode JSON_PTR_LIB pop_back(json_index_t pos); + JSONNode JSON_PTR_LIB pop_back(const json_string & name_t); + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNode JSON_PTR_LIB pop_back_nocase(const json_string & name_t); + #endif + + DECLARE_FOR_ALL_TYPES(JSONNode & operator =) + JSONNode & operator = (const JSONNode &); + + DECLARE_FOR_ALL_TYPES_CONST(bool operator ==) + DECLARE_FOR_ALL_TYPES_CONST(bool operator !=) + + + void nullify(void); + void swap(JSONNode & other); + void merge(JSONNode & other); + void merge(unsigned int num, ...); + JSONNode duplicate(void) const; + void cast(char newtype); + + + //iterator + #ifdef JSON_ITERATORS + #ifndef JSON_LIBRARY + #define json_iterator_ptr(iter) iter.it + #define ptr_to_json_iterator(iter) json_iterator(iter) + struct iterator { + inline iterator& operator ++(void){ ++it; return *this; } + inline iterator& operator --(void){ --it; return *this; } + inline iterator& operator +=(long i){ it += i; return *this; } + inline iterator& operator -=(long i){ it -= i; return *this; } + inline iterator operator ++(int){ + iterator result(*this); + ++it; + return result; + } + inline iterator operator --(int){ + iterator result(*this); + --it; + return result; + } + inline iterator operator +(long i) const { + iterator result(*this); + result.it += i; + return result; + } + inline iterator operator -(long i) const { + iterator result(*this); + result.it -= i; + return result; + } + inline JSONNode& operator [](size_t pos) const { return *it[pos]; }; + inline JSONNode& operator *(void) const { return *(*it); } + inline bool operator == (const iterator & other) const { return it == other.it; } + inline bool operator != (const iterator & other) const { return it != other.it; } + inline bool operator > (const iterator & other) const { return it > other.it; } + inline bool operator >= (const iterator & other) const { return it >= other.it; } + inline bool operator < (const iterator & other) const { return it < other.it; } + inline bool operator <= (const iterator & other) const { return it <= other.it; } + inline iterator & operator = (const iterator & orig){ it = orig.it; return *this; } + iterator (const iterator & orig) : it(orig.it) {} + private: + JSONNode ** it; + iterator(JSONNode ** starter) : it(starter) {} + friend class JSONNode; + }; + typedef iterator json_iterator; + + struct const_iterator { + inline const_iterator& operator ++(void){ ++it; return *this; } + inline const_iterator& operator --(void){ --it; return *this; } + inline const_iterator& operator +=(long i){ it += i; return *this; } + inline const_iterator& operator -=(long i){ it -= i; return *this; } + inline const_iterator operator ++(int){ + const_iterator result(*this); + ++it; + return result; + } + inline const_iterator operator --(int){ + const_iterator result(*this); + --it; + return result; + } + inline const_iterator operator +(long i) const { + const_iterator result(*this); + result.it += i; + return result; + } + inline const_iterator operator -(long i) const { + const_iterator result(*this); + result.it -= i; + return result; + } + inline const JSONNode& operator [](size_t pos) const { return const_cast<const JSONNode&>(*it[pos]); }; + inline const JSONNode& operator *(void) const { return const_cast<const JSONNode&>(*(*it)); } + inline bool operator == (const const_iterator & other) const { return it == other.it; } + inline bool operator != (const const_iterator & other) const { return it != other.it; } + inline bool operator > (const const_iterator & other) const { return it > other.it; } + inline bool operator >= (const const_iterator & other) const { return it >= other.it; } + inline bool operator < (const const_iterator & other) const { return it < other.it; } + inline bool operator <= (const const_iterator & other) const { return it <= other.it; } + inline const_iterator & operator =(const const_iterator & orig){ it = orig.it; return *this; } + const_iterator (const const_iterator & orig) : it(orig.it) {} + private: + JSONNode ** it; + const_iterator(JSONNode ** starter) : it(starter) {} + friend class JSONNode; + }; + const_iterator begin(void) const; + const_iterator end(void) const; + + struct reverse_iterator { + inline reverse_iterator& operator ++(void){ --it; return *this; } + inline reverse_iterator& operator --(void){ ++it; return *this; } + inline reverse_iterator& operator +=(long i){ it -= i; return *this; } + inline reverse_iterator& operator -=(long i){ it += i; return *this; } + inline reverse_iterator operator ++(int){ + reverse_iterator result(*this); + --it; + return result; + } + inline reverse_iterator operator --(int){ + reverse_iterator result(*this); + ++it; + return result; + } + inline reverse_iterator operator +(long i) const { + reverse_iterator result(*this); + result.it -= i; + return result; + } + inline reverse_iterator operator -(long i) const { + reverse_iterator result(*this); + result.it += i; + return result; + } + inline JSONNode& operator [](size_t pos) const { return *it[pos]; }; + inline JSONNode& operator *(void) const { return *(*it); } + inline bool operator == (const reverse_iterator & other) const { return it == other.it; } + inline bool operator != (const reverse_iterator & other) const { return it != other.it; } + inline bool operator < (const reverse_iterator & other) const { return it > other.it; } + inline bool operator <= (const reverse_iterator & other) const { return it >= other.it; } + inline bool operator > (const reverse_iterator & other) const { return it < other.it; } + inline bool operator >= (const reverse_iterator & other) const { return it <= other.it; } + inline reverse_iterator & operator = (const reverse_iterator & orig){ it = orig.it; return *this; } + reverse_iterator (const reverse_iterator & orig) : it(orig.it) {} + private: + JSONNode ** it; + reverse_iterator(JSONNode ** starter) : it(starter) {} + friend class JSONNode; + }; + reverse_iterator rbegin(void); + reverse_iterator rend(void); + + struct reverse_const_iterator { + inline reverse_const_iterator& operator ++(void){ --it; return *this; } + inline reverse_const_iterator& operator --(void){ ++it; return *this; } + inline reverse_const_iterator& operator +=(long i){ it -= i; return *this; } + inline reverse_const_iterator& operator -=(long i){ it += i; return *this; } + inline reverse_const_iterator operator ++(int){ + reverse_const_iterator result(*this); + --it; + return result; + } + inline reverse_const_iterator operator --(int){ + reverse_const_iterator result(*this); + ++it; + return result; + } + inline reverse_const_iterator operator +(long i) const { + reverse_const_iterator result(*this); + result.it -= i; + return result; + } + inline reverse_const_iterator operator -(long i) const { + reverse_const_iterator result(*this); + result.it += i; + return result; + } + inline const JSONNode& operator [](size_t pos) const { return const_cast<const JSONNode&>(*it[pos]); }; + inline const JSONNode& operator *(void) const { return const_cast<const JSONNode&>(*(*it)); } + inline bool operator == (const reverse_const_iterator & other) const { return it == other.it; } + inline bool operator != (const reverse_const_iterator & other) const { return it != other.it; } + inline bool operator < (const reverse_const_iterator & other) const { return it > other.it; } + inline bool operator <= (const reverse_const_iterator & other) const { return it >= other.it; } + inline bool operator > (const reverse_const_iterator & other) const { return it < other.it; } + inline bool operator >= (const reverse_const_iterator & other) const { return it <= other.it; } + inline reverse_const_iterator & operator = (const reverse_const_iterator & orig){ it = orig.it; return *this; } + reverse_const_iterator (const reverse_const_iterator & orig) : it(orig.it) {} + private: + JSONNode ** it; + reverse_const_iterator(JSONNode ** starter) : it(starter) {} + friend class JSONNode; + }; + reverse_const_iterator rbegin(void) const; + reverse_const_iterator rend(void) const; + + const_iterator find(const json_string & name_t) const; + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + const_iterator find_nocase(const json_string & name_t) const; + #endif + + reverse_iterator erase(reverse_iterator pos); + reverse_iterator erase(reverse_iterator start, const reverse_iterator & end); + + iterator insert(iterator pos, const JSONNode & x); + reverse_iterator insert(reverse_iterator pos, const JSONNode & x); + iterator insert(iterator pos, const reverse_iterator & _start, const reverse_iterator & _end); + reverse_iterator insert(reverse_iterator pos, const iterator & _start, const iterator & _end); + reverse_iterator insert(reverse_iterator pos, const reverse_iterator & _start, const reverse_iterator & _end); + + json_iterator insert(json_iterator pos, const const_iterator & _start, const const_iterator & _end); + reverse_iterator insert(reverse_iterator pos, const const_iterator & _start, const const_iterator & _end); + json_iterator insert(json_iterator pos, const reverse_const_iterator & _start, const reverse_const_iterator & _end); + reverse_iterator insert(reverse_iterator pos, const reverse_const_iterator & _start, const reverse_const_iterator & _end); + #else + typedef JSONNode** json_iterator; + #define json_iterator_ptr(iter) iter + #define ptr_to_json_iterator(iter) iter + json_iterator insert(json_iterator pos, JSONNode * x); + #endif + + json_iterator begin(void); + json_iterator end(void); + + json_iterator find(const json_string & name_t); + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + json_iterator find_nocase(const json_string & name_t); + #endif + json_iterator erase(json_iterator pos); + json_iterator erase(json_iterator start, const json_iterator & end); + json_iterator insert(json_iterator pos, const json_iterator & _start, const json_iterator & _end); + #endif + + + #ifdef JSON_MUTEX_CALLBACKS + static void register_mutex_callbacks(json_mutex_callback_t lock, json_mutex_callback_t unlock, void * manager_lock); + #ifdef JSON_MUTEX_MANAGE + static void register_mutex_destructor(json_mutex_callback_t destroy); + #endif + static void set_global_mutex(void * mutex); + void set_mutex(void * mutex); + void lock(int thread); + void unlock(int thread); + struct auto_lock { + public: + auto_lock(JSONNode & node, int thread) : mynode(&node), mythread(thread){ + mynode -> lock(mythread); + } + auto_lock(JSONNode * node, int thread) : mynode(node), mythread(thread){ + mynode -> lock(mythread); + } + ~auto_lock(void){ + mynode -> unlock(mythread); + } + private: + auto_lock & operator = (const auto_lock &); + auto_lock(const auto_lock &); + JSONNode * mynode; + int mythread; + }; + static void * getThisLock(JSONNode * pthis); + #endif + + #ifdef JSON_UNIT_TEST + static int getNodeAllocationCount(void); + static int getNodeDeallocationCount(void); + static int getInternalAllocationCount(void); + static int getInternalDeallocationCount(void); + static void incAllocCount(void); + static void decAllocCount(void); + static void incinternalAllocCount(void); + static void decinternalAllocCount(void); + #endif + + #ifdef JSON_WRITER + json_string write(void); + json_string write_formatted(void); + #endif + + #ifdef JSON_DEBUG + #ifndef JSON_LIBRARY + JSONNode dump(void) const; + #endif + #endif + static void deleteJSONNode(JSONNode * ptr); + static JSONNode * newJSONNode_Shallow(const JSONNode & orig); +JSON_PRIVATE + static JSONNode * newJSONNode(const JSONNode & orig JSON_MUTEX_COPY_DECL2); + static JSONNode * newJSONNode(internalJSONNode * internal_t); + //used by JSONWorker + JSONNode(const json_string & unparsed) : internal(internalJSONNode::newInternal(unparsed)) { //root, specialized because it can only be array or node + incAllocCount(); + } + JSONNode(internalJSONNode * internal_t) : internal(internal_t){ //do not increment anything, this is only used in one case and it's already taken care of + incAllocCount(); + } + JSONNode(bool, JSONNode & orig); + + void decRef(void); //decrements internal's counter, deletes it if needed + #ifdef JSON_REF_COUNT + void makeUniqueInternal(void); //makes internal it's own + void merge(JSONNode * other); + #endif + + #ifdef JSON_DEBUG + #ifndef JSON_LIBRARY + JSONNode dump(size_t & totalmemory); + #endif + #endif + + #ifdef JSON_ITERATORS + #ifndef JSON_LIBRARY + json_iterator insertFRR(json_iterator pos, JSONNode ** const _start, JSONNode ** const _end); + reverse_iterator insertRRR(reverse_iterator pos, JSONNode ** const _start, JSONNode ** const _end); + reverse_iterator insertRFF(reverse_iterator pos, JSONNode ** const _start, JSONNode ** const _end); + #endif + json_iterator insertFFF(json_iterator pos, JSONNode ** const _start, JSONNode ** const _end); + #endif + + mutable internalJSONNode * internal; + friend class JSONWorker; + friend class internalJSONNode; +}; + + + +/* + Implementations are here to keep the class declaration cleaner. They can't be placed in a different + file because they are inlined. +*/ + +inline JSONNode::JSONNode(char mytype) : internal(internalJSONNode::newInternal(mytype)) { + JSON_ASSERT((mytype == JSON_NULL) || + (mytype == JSON_STRING) || + (mytype == JSON_NUMBER) || + (mytype == JSON_BOOL) || + (mytype == JSON_ARRAY) || + (mytype == JSON_NODE), JSON_TEXT("Not a proper JSON type")); + incAllocCount(); +} + +inline JSONNode::JSONNode(const JSONNode & orig): internal(orig.internal -> incRef()) { + incAllocCount(); +} + +//this allows a temp node to simply transfer its contents, even with ref counting off +inline JSONNode::JSONNode(bool, JSONNode & orig): internal(orig.internal){ + orig.internal = 0; + incAllocCount(); +} + +inline JSONNode::~JSONNode(void){ + if (internal) decRef(); + decAllocCount(); +} + +inline json_index_t JSONNode::size(void) const { + JSON_CHECK_INTERNAL(); + return internal -> size(); +} + +inline bool JSONNode::empty(void) const { + JSON_CHECK_INTERNAL(); + return internal -> empty(); +} + +inline void JSONNode::clear(void){ + JSON_CHECK_INTERNAL(); + if (!empty()) { + makeUniqueInternal(); + internal -> Children.clear(); + } +} + +inline unsigned char JSONNode::type(void) const { + JSON_CHECK_INTERNAL(); + return internal -> type(); +} + +inline json_string JSONNode::name(void) const { + JSON_CHECK_INTERNAL(); + return internal -> name(); +} + +inline void JSONNode::set_name(const json_string & newname){ + JSON_CHECK_INTERNAL(); + makeUniqueInternal(); + internal -> setname(newname); +} + +#ifdef JSON_COMMENTS + inline void JSONNode::set_comment(const json_string & newname){ + JSON_CHECK_INTERNAL(); + makeUniqueInternal(); + internal -> setcomment(newname); + } + + inline json_string JSONNode::get_comment(void) const { + JSON_CHECK_INTERNAL(); + return internal -> getcomment(); + } +#endif + +inline json_string JSONNode::as_string(void) const { + JSON_CHECK_INTERNAL(); + return internal -> as_string(); +} + +inline long JSONNode::as_int(void) const { + JSON_CHECK_INTERNAL(); + return internal -> as_int(); +} + +inline json_number JSONNode::as_float(void) const { + JSON_CHECK_INTERNAL(); + return internal -> as_float(); +} + +inline bool JSONNode::as_bool(void) const { + JSON_CHECK_INTERNAL(); + return internal -> as_bool(); +} + +#ifdef JSON_BINARY + inline void JSONNode::set_binary(const unsigned char * bin, json_index_t bytes){ + JSON_CHECK_INTERNAL(); + *this = JSONBase64::json_encode64(bin, bytes); + } + + inline std::string JSONNode::as_binary(void) const { + JSON_ASSERT_SAFE(type() == JSON_STRING, JSON_TEXT("using as_binary for a non-string type"), return EMPTY_STRING2;); + JSON_CHECK_INTERNAL(); + return JSONBase64::json_decode64(as_string()); + } +#endif + +inline JSONNode & JSONNode::operator[](const json_string & name_t){ + JSON_CHECK_INTERNAL(); + makeUniqueInternal(); + return *(*(internal -> at(name_t))); +} + +inline const JSONNode & JSONNode::operator[](const json_string & name_t) const { + JSON_CHECK_INTERNAL(); + return *(*(internal -> at(name_t))); +} + +#ifdef JSON_LIBRARY +inline void JSONNode::push_back(JSONNode * child){ +#else +inline void JSONNode::push_back(const JSONNode & child){ +#endif + JSON_CHECK_INTERNAL(); + makeUniqueInternal(); + internal -> push_back(child); +} + +inline void JSONNode::reserve(json_index_t size){ + makeUniqueInternal(); + internal -> reserve(size); +} + +inline JSONNode & JSONNode::operator = (const JSONNode & orig){ + JSON_CHECK_INTERNAL(); + #ifdef JSON_REF_COUNT + if (internal == orig.internal) return *this; //don't want it accidentally deleting itself + #endif + decRef(); //dereference my current one + internal = orig.internal -> incRef(); //increase reference of original + return *this; +} + +#ifndef JSON_LIBRARY + inline JSONNode & JSONNode::operator = (const json_char * val){ + JSON_CHECK_INTERNAL(); + *this = json_string(val); + return *this; + } +#endif + +#define NODE_SET_TYPED(type)\ + inline JSONNode & JSONNode::operator = (type val){\ + JSON_CHECK_INTERNAL();\ + makeUniqueInternal();\ + internal -> Set(val);\ + return *this;\ + } + +IMPLEMENT_FOR_ALL_TYPES(NODE_SET_TYPED) + + +/* + This section is the equality operators +*/ + +#define NODE_CHECK_EQUALITY(type)\ + inline bool JSONNode::operator == (type val) const {\ + JSON_CHECK_INTERNAL();\ + return internal -> IsEqualToNum<type>(val);\ + } + +IMPLEMENT_FOR_ALL_NUMBERS(NODE_CHECK_EQUALITY) + +inline bool JSONNode::operator == (const json_string & val) const { + JSON_CHECK_INTERNAL(); + return internal -> IsEqualTo(val); +} + +#ifndef JSON_LIBRARY + inline bool JSONNode::operator == (const json_char * val) const { + JSON_CHECK_INTERNAL(); + return *this == json_string(val); + } +#endif + +inline bool JSONNode::operator == (bool val) const { + JSON_CHECK_INTERNAL(); + return internal -> IsEqualTo(val); +} +inline bool JSONNode::operator == (const JSONNode & val) const { + JSON_CHECK_INTERNAL(); + return internal -> IsEqualTo(val.internal); +} + + +/* + This section is the inequality operators +*/ + + +#define NODE_CHECK_INEQUALITY(type)\ + inline bool JSONNode::operator != (type val) const {\ + JSON_CHECK_INTERNAL();\ + return !(*this == val);\ + } + +IMPLEMENT_FOR_ALL_TYPES(NODE_CHECK_INEQUALITY) +NODE_CHECK_INEQUALITY(const JSONNode &) +#ifndef JSON_LIBRARY + NODE_CHECK_INEQUALITY(const json_char * ) +#endif + +inline void JSONNode::nullify(void){ + JSON_CHECK_INTERNAL(); + makeUniqueInternal(); + internal -> Nullify(); +} + +inline void JSONNode::swap(JSONNode & other){ + JSON_CHECK_INTERNAL(); + internalJSONNode * temp = other.internal; + other.internal = internal; + internal = temp; + JSON_CHECK_INTERNAL(); +} + +inline void JSONNode::decRef(void){ //decrements internal's counter, deletes it if needed + JSON_CHECK_INTERNAL(); + #ifdef JSON_REF_COUNT + internal -> decRef(); + if (internal -> hasNoReferences()) { + internalJSONNode::deleteInternal(internal); + } + #else + internalJSONNode::deleteInternal(internal); + #endif +} + +#ifdef JSON_REF_COUNT + inline void JSONNode::makeUniqueInternal() { //makes internal it's own + JSON_CHECK_INTERNAL(); + internal = internal -> makeUnique(); //might return itself or a new one that's exactly the same + } +#endif + +#ifdef JSON_ITERATORS + inline JSONNode::json_iterator JSONNode::begin(void){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("iterating a non-iteratable node")); + makeUniqueInternal(); + return json_iterator(internal -> begin()); + } + + inline JSONNode::json_iterator JSONNode::end(void){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("iterating a non-iteratable node")); + makeUniqueInternal(); + return json_iterator(internal -> end()); + } + + #ifndef JSON_LIBRARY + inline JSONNode::const_iterator JSONNode::begin(void) const { + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("iterating a non-iteratable node")); + return JSONNode::const_iterator(internal -> begin()); + } + + inline JSONNode::const_iterator JSONNode::end(void) const { + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("iterating a non-iteratable node")); + return JSONNode::const_iterator(internal -> end()); + } + + inline JSONNode::reverse_iterator JSONNode::rbegin(void){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("iterating a non-iteratable node")); + makeUniqueInternal(); + return JSONNode::reverse_iterator(internal -> end() - 1); + } + + inline JSONNode::reverse_iterator JSONNode::rend(void){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("iterating a non-iteratable node")); + makeUniqueInternal(); + return JSONNode::reverse_iterator(internal -> begin() - 1); + } + + inline JSONNode::reverse_const_iterator JSONNode::rbegin(void) const { + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("iterating a non-iteratable node")); + return JSONNode::reverse_const_iterator(internal -> end() - 1); + } + + inline JSONNode::reverse_const_iterator JSONNode::rend(void) const { + JSON_CHECK_INTERNAL(); + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("iterating a non-iteratable node")); + return JSONNode::reverse_const_iterator(internal -> begin() - 1); + } + + inline JSONNode::iterator JSONNode::insert(json_iterator pos, const const_iterator & _start, const const_iterator & _end){ + return insertFFF(pos, _start.it, _end.it); + } + + inline JSONNode::reverse_iterator JSONNode::insert(reverse_iterator pos, const const_iterator & _start, const const_iterator & _end){ + return insertRFF(pos, _start.it, _end.it); + } + + inline JSONNode::reverse_iterator JSONNode::insert(reverse_iterator pos, const iterator & _start, const iterator & _end){ + return insertRFF(pos, _start.it, _end.it); + } + + inline JSONNode::reverse_iterator JSONNode::insert(reverse_iterator pos, const reverse_const_iterator & _start, const reverse_const_iterator & _end){ + return insertRRR(pos, _start.it, _end.it); + } + + inline JSONNode::reverse_iterator JSONNode::insert(reverse_iterator pos, const reverse_iterator & _start, const reverse_iterator & _end){ + return insertRRR(pos, _start.it, _end.it); + } + + inline JSONNode::iterator JSONNode::insert(json_iterator pos, const reverse_const_iterator & _start, const reverse_const_iterator & _end){ + return insertFRR(pos, _start.it, _end.it); + } + + inline JSONNode::iterator JSONNode::insert(iterator pos, const reverse_iterator & _start, const reverse_iterator & _end){ + return insertFRR(pos, _start.it, _end.it); + } + #endif + + inline JSONNode::json_iterator JSONNode::insert(json_iterator pos, const json_iterator & _start, const json_iterator & _end){ + return insertFFF(pos, json_iterator_ptr(_start), json_iterator_ptr(_end)); + } +#endif + +#ifdef JSON_WRITER + inline json_string JSONNode::write(void){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT_SAFE(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("Writing a non-writable node"), return JSON_TEXT("");); + return internal -> Write(0xFFFFFFFF, true); + } + + inline json_string JSONNode::write_formatted(void){ + JSON_CHECK_INTERNAL(); + JSON_ASSERT_SAFE(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("Writing a non-writable node"), return JSON_TEXT("");); + return internal -> Write(0, true); + } + +#endif + +#ifndef JSON_PREPARSE + inline void JSONNode::preparse(void){ + JSON_CHECK_INTERNAL(); + internal -> preparse(); + } +#endif + +#ifdef JSON_VALIDATE + inline bool JSONNode::validate(void){ + JSON_CHECK_INTERNAL(); + if (type() == JSON_NULL) return false; + JSON_ASSERT_SAFE(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("Validating non root node"), return false;); + #ifndef JSON_PREPARSE + internal -> Fetch(); //will nullify it if it's bad + #endif + if (type() == JSON_NULL) return false; + return internal -> validate(); + } +#endif + +#ifdef JSON_DEBUG + #ifndef JSON_LIBRARY + inline JSONNode JSONNode::dump(void) const { + JSON_CHECK_INTERNAL(); + JSONNode dumpage(JSON_NODE); + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("this"), (long)this))); + size_t total = 0; + JSONNode node = internal -> Dump(total); + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("total bytes used"), total))); + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("bytes used"), sizeof(JSONNode)))); + dumpage.push_back(JSON_NEW(node)); + return dumpage; + } + + inline JSONNode JSONNode::dump(size_t & totalmemory){ + JSON_CHECK_INTERNAL(); + JSONNode dumpage(JSON_NODE); + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("this"), (long)this))); + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("bytes used"), sizeof(JSONNode)))); + dumpage.push_back(JSON_NEW(internal -> Dump(totalmemory))); + return dumpage; + } + #endif +#endif + + +inline void JSONNode::deleteJSONNode(JSONNode * ptr){ + #ifdef JSON_MEMORY_CALLBACKS + ptr -> ~JSONNode(); + libjson_free<JSONNode>(ptr); + #else + delete ptr; + #endif +} + +inline JSONNode * _newJSONNode(const JSONNode & orig){ + #ifdef JSON_MEMORY_CALLBACKS + return new(json_malloc<JSONNode>(1)) JSONNode(orig); + #else + return new JSONNode(orig); + #endif +} + +inline JSONNode * JSONNode::newJSONNode(const JSONNode & orig JSON_MUTEX_COPY_DECL){ + #ifdef JSON_MUTEX_CALLBACKS + if (parentMutex){ + JSONNode * temp = _newJSONNode(orig); + temp -> set_mutex(parentMutex); + return temp; + } + #endif + return _newJSONNode(orig); +} + +inline JSONNode * JSONNode::newJSONNode(internalJSONNode * internal_t){ + #ifdef JSON_MEMORY_CALLBACKS + return new(json_malloc<JSONNode>(1)) JSONNode(internal_t); + #else + return new JSONNode(internal_t); + #endif +} + +inline JSONNode * JSONNode::newJSONNode_Shallow(const JSONNode & orig){ + #ifdef JSON_MEMORY_CALLBACKS + return new(json_malloc<JSONNode>(1)) JSONNode(true, const_cast<JSONNode &>(orig)); + #else + return new JSONNode(true, const_cast<JSONNode &>(orig)); + #endif +} +#endif diff --git a/src/modules/json/JSONNode_Mutex.cpp b/src/modules/json/JSONNode_Mutex.cpp new file mode 100644 index 0000000000..2adb694ca8 --- /dev/null +++ b/src/modules/json/JSONNode_Mutex.cpp @@ -0,0 +1,216 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2009 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "..\..\core\commonheaders.h" +#include <m_json.h> + +#include "libJSON.h" + +#ifdef JSON_MUTEX_CALLBACKS + +json_mutex_callback_t json_lock_callback = 0; +json_mutex_callback_t json_unlock_callback = 0; +void * global_mutex = 0; +void * manager_mutex = 0; + +struct AutoLock { + AutoLock(void){ + json_lock_callback(manager_mutex); + } + ~AutoLock(void){ + json_unlock_callback(manager_mutex); + } +}; + +#include <map> +#ifdef JSON_MUTEX_MANAGE + json_mutex_callback_t json_destroy = 0; + std::map<void *, unsigned int> mutex_manager; + + //make sure that the global mutex is taken care of too + struct auto_global { + auto_global(void){} + ~auto_global(void){ + if (global_mutex){ + JSON_ASSERT_SAFE(json_destroy, JSON_TEXT("No json_destroy in mutex managed mode"), return;); + json_destroy(global_mutex); + } + } + }; + auto_global cleanupGlobal; +#endif + +void JSONNode::register_mutex_callbacks(json_mutex_callback_t lock, json_mutex_callback_t unlock, void * manager_lock){ + json_lock_callback = lock; + json_unlock_callback = unlock; + manager_mutex = manager_lock; +} + +void JSONNode::set_global_mutex(void * mutex){ + global_mutex = mutex; +} + +void JSONNode::set_mutex(void * mutex){ + makeUniqueInternal(); + internal -> _set_mutex(mutex); +} + +std::map<int, std::map<void *, unsigned int> > threadlocks; + +void * JSONNode::getThisLock(JSONNode * pthis){ + if (pthis -> internal -> mylock){ + return pthis -> internal -> mylock; + } + JSON_ASSERT_SAFE(global_mutex, JSON_TEXT("No global_mutex"), return 0;); + return global_mutex; +} + +void JSONNode::lock(int thread){ + JSON_ASSERT_SAFE(json_lock_callback, JSON_TEXT("No locking callback"), return;); + + AutoLock lockControl; + + //first, figure out what needs to be locked + void * thislock = getThisLock(this); + #ifdef JSON_SAFE + if (!thislock) return; + #endif + + //make sure that the same thread isn't locking it more than once (possible due to complex ref counting) + std::map<int, std::map<void *, unsigned int> >::iterator it = threadlocks.find(thread); + if (it == threadlocks.end()) { + std::map<void *, unsigned int> newthread; + newthread[thislock] = 1; + threadlocks.insert(std::pair<int, std::map<void *, unsigned int> >(thread, newthread)); + } else { //this thread already has some things locked, check if the current mutex is + std::map<void *, unsigned int> & newthread = it -> second; + std::map<void *, unsigned int>::iterator locker = newthread.find(thislock); + if (locker == newthread.end()) { //current mutex is not locked, set it to locked + newthread.insert(std::pair<void *, unsigned int>(thislock, 1)); + } else { //it's already locked, don't relock it + ++(locker -> second); + return; //don't try to relock, it will deadlock the program + } + } + + //if I need to, lock it + json_lock_callback(thislock); +} + +void JSONNode::unlock(int thread){ + JSON_ASSERT_SAFE(json_unlock_callback, JSON_TEXT("No unlocking callback"), return;); + + AutoLock lockControl; + + //first, figure out what needs to be locked + void * thislock = getThisLock(this); + #ifdef JSON_SAFE + if (!thislock) return; + #endif + + //get it out of the map + std::map<int, std::map<void *, unsigned int> >::iterator it = threadlocks.find(thread); + JSON_ASSERT_SAFE(it != threadlocks.end(), JSON_TEXT("thread unlocking something it didn't lock"), return;); + + //get the mutex out of the thread + std::map<void *, unsigned int> & newthread = it -> second; + std::map<void *, unsigned int>::iterator locker = newthread.find(thislock); + JSON_ASSERT_SAFE(locker != newthread.end(), JSON_TEXT("thread unlocking mutex it didn't lock"), return;); + + //unlock it + if (--(locker -> second)) return; //other nodes is this same thread still have a lock on it + + //if I need to, unlock it + newthread.erase(locker); + json_unlock_callback(thislock); +} + +#ifdef JSON_MUTEX_MANAGE + void JSONNode::register_mutex_destructor(json_mutex_callback_t destroy){ + json_destroy = destroy; + } +#endif + + +void internalJSONNode::_set_mutex(void * mutex, bool unset){ + if (unset) _unset_mutex(); //for reference counting + mylock = mutex; + if (mutex){ + #ifdef JSON_MUTEX_MANAGE + std::map<void *, unsigned int>::iterator it = mutex_manager.find(mutex); + if (it == mutex_manager.end()) { + mutex_manager.insert(std::pair<void *, unsigned int>(mutex, 1)); + } else { + ++it -> second; + } + #endif + json_foreach(Children, myrunner){ + (*myrunner) -> set_mutex(mutex); + } + } +} + +void internalJSONNode::_unset_mutex(void){ + #ifdef JSON_MUTEX_MANAGE + if (mylock){ + std::map<void *, unsigned int>::iterator it = mutex_manager.find(mylock); + JSON_ASSERT_SAFE(it != mutex_manager.end(), JSON_TEXT("Mutex not managed"), return;); + --it -> second; + if (it -> second == 0){ + JSON_ASSERT_SAFE(json_destroy, JSON_TEXT("You didn't register a destructor for mutexes"), return;); + mutex_manager.erase(it); + } + } + #endif +} + +#ifdef JSON_DEBUG + #ifndef JSON_LIBRARY + JSONNode internalJSONNode::DumpMutex(void) const { + JSONNode mut(JSON_NODE); + mut.set_name(JSON_TEXT("mylock")); + #ifdef JSON_MUTEX_MANAGE + if (mylock){ + mut.push_back(JSON_NEW(JSONNode(JSON_TEXT("this"), (long)mylock))); + std::map<void *, unsigned int>::iterator it = mutex_manager.find(mylock); + if (it == mutex_manager.end()) { + mut.push_back(JSON_NEW(JSONNode(JSON_TEXT("references"), JSON_TEXT("error")))); + } else { + mut.push_back(JSON_NEW(JSONNode(JSON_TEXT("references"), it -> second))); + } + } else { + mut = (long)mylock; + } + #else + mut = (long)mylock; + #endif + return mut; + } + #endif +#endif + +#else + #ifdef JSON_MUTEX_MANAGE + #error You can not have JSON_MUTEX_MANAGE on without JSON_MUTEX_CALLBACKS + #endif +#endif diff --git a/src/modules/json/JSONOptions.h b/src/modules/json/JSONOptions.h new file mode 100644 index 0000000000..3c5bb8168f --- /dev/null +++ b/src/modules/json/JSONOptions.h @@ -0,0 +1,214 @@ +#ifndef JSON_OPTIONS_H +#define JSON_OPTIONS_H + +#define snprintf _snprintf + +/** + * This file holds all of the compiling options for easy access and so + * that you don't have to remember them, or look them up all the time + */ + + +/* + * JSON_LIBRARY must be declared if libjson is compiled as a static or dynamic + * library. This exposes a C-style interface, but none of the inner workings of libjson + */ +#define JSON_LIBRARY + + +/* + * JSON_DEBUG is used to perform extra error checking. Because libjson usually + * does on the fly parsing, validation is impossible, so this option will allow + * you to register an error callback so that you can record what is going wrong + * before the library crashes. This option does not protect from these errors, + * it simply tells you about them, which is nice for debugging, but not preferable + * for release candidates + */ +//#define JSON_DEBUG + + +/* + * JSON_SAFE performs similarly to JSON_DEBUG, except this option does protect + * from the errors that it encounters. This option is recommended for those who + * feel it's possible for their program to encounter invalid json. + */ +#define JSON_SAFE + + +/* + * JSON_STDERROR routes error messages to cerr instead of a callback, this + * option hides the callback registering function. This will usually display + * messages in the console + */ +//#define JSON_STDERROR + + +/* + * JSON_PREPARSE causes all parsing to be done immediately. By default, libjson + * parses nodes on the fly as they are needed, this makes parsing much faster if + * your program gets a lot of information that it doesn't need. An example of + * this would be a client application communicating with a server if the server + * returns things like last modified date and other things that you don't use. + */ +//#define JSON_PREPARSE + + +/* + * JSON_LESS_MEMORY will force libjson to let go of memory as quickly as it can + * this is recommended for software that has to run on less than optimal machines. + * It will cut libjson's memory usage by about 20%, but also run slightly slower. + * It's recommended that you also compile using the -Os option, as this will also + * reduce the size of the library + */ +//#define JSON_LESS_MEMORY + + +/* + * JSON_UNICODE tells libjson to use wstrings instead of regular strings, this + * means that libjson supports the full array of unicode characters, but also takes + * much more memory and processing power. + */ +//#define JSON_UNICODE + + +/* + * JSON_REF_COUNT causes libjson to reference count JSONNodes, which makes copying + * and passing them around much faster. It is recommended that this stay on for + * most uses + */ +#define JSON_REF_COUNT + + +/* + * JSON_BINARY is used to support binary, which is base64 encoded and decoded by libjson, + * if this option is not turned on, no base64 support is included + */ +//#define JSON_BINARY + + +/* + * JSON_MEMORY_CALLBACKS exposes functions to register callbacks for allocating, resizing, + * and freeing memory. Because libjson is designed for costomizability, it is feasible + * that some users would like to further add speed by having the library utilize a memory + * pool. With this option turned on, the default behavior is still done internally unless + * a callback is registered. So you can have this option on and mot use it. + */ +#define JSON_MEMORY_CALLBACKS + +/* + * JSON_MEMORY_MANAGE is used to create functionality to automatically track and clean + * up memory that has been allocated by the user. This includes strings, binary data, and + * nodes. It also exposes bulk delete functions. + */ +//#define JSON_MEMORY_MANAGE + + +/* + * JSON_MUTEX_CALLBACKS exposes functions to register callbacks to lock and unlock + * mutexs and functions to lock and unlock JSONNodes and all of it's children. This + * does not prevent other threads from accessing the node, but will prevent them from + * locking it. It is much easier for the end programmer to allow libjson to manage + * your mutexs because of reference counting and manipulating trees, libjson automatically + * tracks mutex controls for you, so you only ever lock what you need to + */ +//#define JSON_MUTEX_CALLBACKS + + +/* + * JSON_MUTEX_MANAGE lets you set mutexes and forget them, libjson will not only keep + * track of the mutex, but also keep a count of how many nodes are using it, and delete + * it when there are no more references + */ +//#define JSON_MUTEX_MANAGE + + +/* + * JSON_ISO_STRICT turns off all code that uses non-standard C++. This removes all + * references to long long and long double as well as a few others + */ +//#define JSON_ISO_STRICT + +/* + * JSON_ITERATORS turns on all of libjson's iterating functionality. This would usually + * only be turned off while compiling for use with C + */ +//#define JSON_ITERATORS + + +/* + * JSON_WRITER turns on libjson's writing capabilties. Without this libjson can only + * read and parse json, this allows it to write back out + */ +#define JSON_WRITER + + +/* + * JSON_NEWLINE affects how libjson writes. If this option is turned on, libjson + * will use whatever it's defined as for the newline signifier, otherwise, it will use + * standard unix \n. + */ +//#define JSON_NEWLINE "\r\n" //\r\n is standard for most windows and dos programs + + +/* + * JSON_COMMENTS tells libjson to store and write comments. libjson always supports + * parsing json that has comments in it as it simply ignores them, but with this option + * it keeps the comments and allows you to insert further comments + */ +//#define JSON_COMMENTS + + +/* + * JSON_INDENT affects how libjson writes. If this option is turned on, libjson + * will use \t to indent formatted json, otherwise it will use the number of characters + * that you specify. If this is not turned on, then it will use the tab (\t) character + */ +//#define JSON_INDENT " " + + +/* + * JSON_WRITE_BASH_COMMENTS will cause libjson to write all comments in bash (#) style + * if this option is not turned on, then it will use C-style comments. Bash comments are + * all single line + */ +//#define JSON_WRITE_BASH_COMMENTS + + +/* + * JSON_WRITE_SINGLE_LINE_COMMENTS will cause libjson to write all comments in using // + * notation, or (#) if that option is on. Some parsers do not support multiline C comments + * although, this option is not needed for bash comments, as they are all single line anyway + */ +//#define JSON_WRITE_SINGLE_LINE_COMMENTS + + +/* + * JSON_VALIDATE turns on validation features of libjson. This option requires JSON_SAFE + */ +//#define JSON_VALIDATE + + +/* + * JSON_CASE_INSENSITIVE_FUNCTIONS turns on funtions for finding child nodes in a case- + * insenititve way + */ +//#define JSON_CASE_INSENSITIVE_FUNCTIONS + + +/* + * JSON_UNIT_TEST is used to maintain and debug the libjson. It makes all private + * members and functions public so that tests can do checks of the inner workings + * of libjson. This should not be turned on by end users. + */ +//#define JSON_UNIT_TEST + + +/* + * JSON_INDEX_TYPE allows you th change the size type for the children functions. If this + * option is not used then unsigned int is used. This option is useful for cutting down + * on memory, or using huge numbers of child nodes (over 4 billion) + */ +//#define JSON_INDEX_TYPE unsigned int + +#endif + diff --git a/src/modules/json/JSONWorker.cpp b/src/modules/json/JSONWorker.cpp new file mode 100644 index 0000000000..c99cd38ee2 --- /dev/null +++ b/src/modules/json/JSONWorker.cpp @@ -0,0 +1,701 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+
+#include "JSONWorker.h" + +#ifdef JSON_VALIDATE +JSONNode JSONWorker::validate(const json_string & json){ + JSONNode res = parse(json); + if (!res.validate()) { + throw std::invalid_argument(EMPTY_STRING2); + } + return JSONNode(true, res); //forces it to simply return the original interal, even with ref counting off +} +#endif + +JSONNode JSONWorker::parse(const json_string & json){ + json_auto<json_char> s; + #if defined JSON_DEBUG || defined JSON_SAFE + json_char lastchar; + s.set(RemoveWhiteSpace(json, lastchar)); + #else + s.set(RemoveWhiteSpace(json)); + #endif + + #ifdef JSON_COMMENTS + json_char firstchar = s.ptr[0]; + json_string _comment; + json_char * runner = s.ptr; + if (firstchar == '\5') { //multiple comments will be consolidated into one + newcomment: + while(*(++runner) != '\5') { + JSON_ASSERT(*runner, JSON_TEXT("Removing white space failed")); + _comment += *runner; + } + firstchar = *(++runner); //step past the trailing tag + if (firstchar == '\5') { + _comment += '\n'; + goto newcomment; + } + } + #else + const json_char firstchar = s.ptr[0]; + #endif + + switch (firstchar){ + case '{': + case '[': + #if defined JSON_DEBUG || defined JSON_SAFE + if (firstchar == '[') { + if (lastchar != ']') { + JSON_FAIL(JSON_TEXT("Missing final ]")); + break; + } + } else { + if (lastchar != '}') { + JSON_FAIL(JSON_TEXT("Missing final }")); + break; + } + } + #endif + #ifdef JSON_COMMENTS + JSONNode foo(runner); + foo.set_comment(_comment); + return JSONNode(true, foo); //forces it to simply return the original interal, even with ref counting off + #else + return JSONNode(s.ptr); + #endif + } + + JSON_FAIL(JSON_TEXT("Not JSON!")); + throw std::invalid_argument(EMPTY_STRING2); +} + +#define QUOTECASE()\ + case JSON_TEXT('\"'):\ + while (*(++p) != JSON_TEXT('\"')) {\ + JSON_ASSERT_SAFE(*p, JSON_TEXT("Null terminator inside of a quotation"), return json_string::npos;);\ + }\ + break; + +#ifdef JSON_DEBUG + #define NULLCASE(error)\ + case JSON_TEXT('\0'):\ + JSON_FAIL_SAFE(error, return json_string::npos;);\ + break; +#else + #define NULLCASE(error) +#endif + +#define BRACKET(left, right)\ + case left: {\ + size_t brac = 1;\ + while (brac){\ + switch (*(++p)) {\ + case right:\ + --brac;\ + break;\ + case left:\ + ++brac;\ + break;\ + QUOTECASE()\ + NULLCASE(JSON_TEXT("Null terminator inside of a bracket"))\ + }\ + }\ + break;}\ + case right:\ + return json_string::npos; + +size_t JSONWorker::FindNextRelevant(json_char ch, const json_string & value_t, const size_t pos){ + const json_char * start = value_t.c_str(); + const json_char * p = start + pos; //start at the correct offset + do { + if (*p == ch) return p - start; + switch (*p){ + BRACKET(JSON_TEXT('['), JSON_TEXT(']')) + BRACKET(JSON_TEXT('{'), JSON_TEXT('}')) + QUOTECASE() + } + } while(*(++p)); + return json_string::npos; +} + +#ifdef JSON_COMMENTS + #define COMMENT_DELIMITER() *runner++ = '\5' + #define AND_RUNNER ,runner + inline void SingleLineComment(const json_char * & p, json_char * & runner){ + COMMENT_DELIMITER(); + while((*(++p)) && (*p != JSON_TEXT('\n'))) { + *runner++ = *p; + } + COMMENT_DELIMITER(); + } +#else + #define COMMENT_DELIMITER() (void)0 + #define AND_RUNNER +#endif + +inline void SingleLineComment(const json_char * & p){ + while((*(++p)) && (*p != JSON_TEXT('\n'))); +} + + +#if defined JSON_DEBUG || defined JSON_SAFE + json_char * JSONWorker::RemoveWhiteSpace(const json_string & value_t, json_char & last){ +#else + json_char * JSONWorker::RemoveWhiteSpace(const json_string & value_t){ +#endif + json_char * result; + json_char * runner = result = json_malloc<json_char>(value_t.length() + 1); //dealing with raw memory is faster than adding to a json_string + JSON_ASSERT(result, JSON_TEXT("Out of memory")); + const json_char * p = value_t.c_str(); + while(*p){ + switch(*p){ + case JSON_TEXT(' '): //defined as white space + case JSON_TEXT('\t'): //defined as white space + case JSON_TEXT('\n'): //defined as white space + case JSON_TEXT('\r'): //defined as white space + break; + case JSON_TEXT('/'): //a C comment + if (*(++p) == JSON_TEXT('*')) { //a multiline comment + COMMENT_DELIMITER(); + while ((*(++p) != JSON_TEXT('*')) || (*(p + 1) != JSON_TEXT('/'))) { + JSON_ASSERT_SAFE(*p, JSON_TEXT("Null terminator inside of a multiline quote"), COMMENT_DELIMITER(); goto endofloop;); + *runner++ = *p; + } + ++p; + COMMENT_DELIMITER(); + break; + } + //Should be a single line C comment, so let it fall through to use the bash comment stripper + JSON_ASSERT_SAFE(*p == JSON_TEXT('/'), JSON_TEXT("stray / character, not quoted, or a comment"), goto endofloop;); + case JSON_TEXT('#'): //a bash comment + SingleLineComment(p AND_RUNNER); + break; + case JSON_TEXT('\"'): //a quote + *runner++ = JSON_TEXT('\"'); + while(*(++p) != JSON_TEXT('\"')) { //find the end of the quotation, as white space is preserved within it + JSON_ASSERT_SAFE(*p, JSON_TEXT("Null terminator inside of a quotation"), goto endofloop;); + switch(*p){ + case JSON_TEXT('\\'): + *runner++ = JSON_TEXT('\\'); + *runner++ = (*++p == JSON_TEXT('\"')) ? JSON_TEXT('\1') : *p; //an escaped quote will reak havoc will all of my searching functions, so change it into an illegal character in JSON for convertion later on + break; + default: + *runner++ = *p; + break; + } + } + //no break, let it fall through so that the trailing quote gets added + default: + JSON_ASSERT_SAFE((unsigned json_char)*p >= 32, JSON_TEXT("Invalid JSON character detected (lo)"), goto endofloop;); + JSON_ASSERT_SAFE((unsigned json_char)*p <= 126, JSON_TEXT("Invalid JSON character detected (hi)"), goto endofloop;); + *runner++ = *p; + break; + } + ++p; + } + #ifdef JSON_SAFE + endofloop: + #endif + #if defined JSON_DEBUG || defined JSON_SAFE + last = *(runner - 1); + #endif + *runner = JSON_TEXT('\0'); + return result; + } + +json_string JSONWorker::RemoveWhiteSpaceAndComments(const json_string & value_t){ + json_string result; + result.reserve(value_t.length()); + const json_char * p = value_t.c_str(); + while(*p){ + switch(*p){ + case JSON_TEXT(' '): //defined as white space + case JSON_TEXT('\t'): //defined as white space + case JSON_TEXT('\n'): //defined as white space + case JSON_TEXT('\r'): //defined as white space + break; + case JSON_TEXT('/'): //a C comment + if (*(++p) == JSON_TEXT('*')) { //a multiline comment + while ((*(++p) != JSON_TEXT('*')) || (*(p + 1) != JSON_TEXT('/'))) { + JSON_ASSERT_SAFE(*p, JSON_TEXT("Null terminator inside of a multiline quote"), goto endofloop;); + } + ++p; + break; + } + //Should be a single line C comment, so let it fall through to use the bash comment stripper + JSON_ASSERT_SAFE(*p == JSON_TEXT('/'), JSON_TEXT("stray / character, not quoted, or a comment"), goto endofloop;); + case JSON_TEXT('#'): //a bash comment + SingleLineComment(p); + break; + case JSON_TEXT('\"'): //a quote + result += JSON_TEXT('\"'); + while(*(++p) != JSON_TEXT('\"')) { //find the end of the quotation, as white space is preserved within it + JSON_ASSERT(*p, JSON_TEXT("Null terminator inside of a quotation")); + switch(*p){ + case JSON_TEXT('\\'): + result += JSON_TEXT('\\'); + result += (*++p == JSON_TEXT('\"')) ? JSON_TEXT('\1') : *p; //an escaped quote will reak havoc will all of my searching functions, so change it into an illegal character in JSON for convertion later on + break; + default: + result += *p; + break; + } + } + //no break, let it fall through so that the trailing quote gets added + default: + JSON_ASSERT_SAFE((unsigned json_char)*p >= 32, JSON_TEXT("Invalid JSON character detected (lo)"), goto endofloop;); + JSON_ASSERT_SAFE((unsigned json_char)*p <= 126, JSON_TEXT("Invalid JSON character detected (hi)"), goto endofloop;); + result += *p; + break; + } + ++p; + } + #ifdef JSON_SAFE + endofloop: + #endif + return result; +} + +/* + These three functions analyze json_string literals and convert them into std::strings + This includes dealing with special characters and utf characters + */ +#ifdef JSON_UNICODE + inline unsigned json_char SurrogatePair(const unsigned json_char hi, const unsigned json_char lo){ + JSON_ASSERT(sizeof(unsigned int) == 4, JSON_TEXT("size of unsigned int is not 32-bit")); + JSON_ASSERT(sizeof(unsigned json_char) == 4, JSON_TEXT("size of json_char is not 32-bit")); + return (((hi << 10) & 0x1FFC00) + 0x10000) | lo & 0x3FF; + } + + json_string JSONWorker::UTF(const json_char * & pos){ + json_string result; + unsigned json_char first = UTF8(pos); + if ((*(pos + 1) == '\\') && (*(pos + 2) == 'u')) { + pos += 2; + unsigned json_char second = UTF8(pos); + //surrogate pair, not two characters + if ((first > 0xD800) && (first < 0xDBFF) && (second > 0xDC00) && (second < 0xDFFF)) { + result += SurrogatePair(first, second); + } else { + result += first; + result += second; + } + } else { + result += first; + } + JSON_ASSERT(!result.empty(), JSON_TEXT("missing case, somehow UTF returned empty")); + return result; + } +#endif + +unsigned json_char JSONWorker::UTF8(const json_char * & pos){ + #ifdef JSON_UNICODE + ++pos; + unsigned json_char temp = Hex(pos) << 8; + ++pos; + return temp | Hex(pos); + #else + JSON_ASSERT(*(pos + 1) == JSON_TEXT('0'), JSON_TEXT("wide utf character (hihi)")); + JSON_ASSERT(*(pos + 2) == JSON_TEXT('0'), JSON_TEXT("wide utf character (hilo)")); + pos += 3; + return Hex(pos); + #endif +} + +static char szU8Buffer[10]; + +json_char* JSONWorker::UTF8_2(const json_char * & pos){ + #ifdef JSON_UNICODE + ++pos; + unsigned json_char temp = Hex(pos) << 8; + ++pos; + *szU8Buffer= temp | Hex(pos); + szU8Buffer[1]=0; + return szU8Buffer; + #else + union { + unsigned short uChar; + unsigned char uByte[2]; + }; + pos++; + strncpy(szU8Buffer+5,pos,4); + szU8Buffer[9]=0; + uChar=strtoul(szU8Buffer+5,NULL,16); + if (uChar<0x80) { + szU8Buffer[0]=uChar; + szU8Buffer[1]=0; + } else if (uChar<0x7ff) { + szU8Buffer[0]=0xc0+(uByte[1]<<2)+(uByte[0]>>6); + szU8Buffer[1]=0x80+(uByte[0]&0x3f); + szU8Buffer[2]=0; + } else { + szU8Buffer[0]=0xe0+(uByte[1]>>4); + szU8Buffer[1]=0x80+((uByte[1]&0x0f)<<2)+(uByte[0]>>6); + szU8Buffer[2]=0x80+(uByte[0]&0x3f); + szU8Buffer[3]=0; + }
+ + pos += 3; + return szU8Buffer; + #endif +} + + +json_char JSONWorker::Hex(const json_char * & pos){ + /* + takes the numeric value of the next two characters and convert them + \u0058 becomes 0x58 + + In case of \u, it's SpecialChar's responsibility to move past the first two chars + as this method is also used for \x + */ + //First character + unsigned json_char hi = *pos++ - 48; + if (hi > 48){ //A-F don't immediately follow 0-9, so have to pull them down a little + hi -= 39; + } else if (hi > 9){ //neither do a-f + hi -= 7; + } + //second character + unsigned json_char lo = *pos - 48; + if (lo > 48){ //A-F don't immediately follow 0-9, so have to pull them down a little + lo -= 39; + } else if (lo > 9){ //neither do a-f + lo -= 7; + } + //combine them + return (json_char)((hi << 4) | lo); +} + +inline json_char FromOctal(const json_char * & str){ + JSON_ASSERT(json_strlen(str) > 3, JSON_TEXT("Octal will go out of bounds")); + const unsigned json_char top = ((unsigned json_char)(*(str++) - 48)); + const unsigned json_char middle = (unsigned json_char)(*(str++) - 48); + const unsigned json_char bottom = (unsigned json_char)(*str - 48); + return (json_char)((top << 6) | (middle << 3) | bottom); +} + +void JSONWorker::SpecialChar(const json_char * & pos, json_string & res){ + /* + Since JSON uses forward slash escaping for special characters within strings, I have to + convert these escaped characters into C characters + */ + switch(*pos){ + case JSON_TEXT('\1'): //quote character (altered by RemoveWhiteSpace) + res += JSON_TEXT('\"'); + break; + case JSON_TEXT('t'): //tab character + res += JSON_TEXT('\t'); + break; + case JSON_TEXT('n'): //newline character + res += JSON_TEXT('\n'); + break; + case JSON_TEXT('r'): //return character + res += JSON_TEXT('\r'); + break; + case JSON_TEXT('\\'): //backslash + res += JSON_TEXT('\\'); + break; + case JSON_TEXT('/'): //forward slash + res += JSON_TEXT('/'); + break; + case JSON_TEXT('b'): //backspace + res += JSON_TEXT('\b'); + break; + case JSON_TEXT('f'): //formfeed + res += JSON_TEXT('\f'); + break; + case JSON_TEXT('v'): //vertical tab + res += JSON_TEXT('\v'); + break; + case JSON_TEXT('\''): //apostrophe + res += JSON_TEXT('\''); + break; + case JSON_TEXT('x'): //hexidecimal ascii code + res += Hex(++pos); + break; + case JSON_TEXT('u'): //utf character + #ifdef JSON_UNICODE + res += UTF(pos); + #else + //res += UTF8(pos); + res.append(UTF8_2(pos)); + #endif + break; + + //octal encoding + case JSON_TEXT('1'): + case JSON_TEXT('2'): + case JSON_TEXT('3'): + case JSON_TEXT('4'): + case JSON_TEXT('5'): + case JSON_TEXT('6'): + case JSON_TEXT('7'): + case JSON_TEXT('0'): + res += FromOctal(pos); + break; + + #ifdef JSON_DEBUG + default: + JSON_FAIL(JSON_TEXT("Unsupported escaped character")); + #endif + } +} + +#ifdef JSON_LESS_MEMORY + inline void doflag(const internalJSONNode * flag, bool which, bool x){ + if (which){ + flag -> _name_encoded = x; + } else { + flag -> _string_encoded = x; + } + } + + json_string JSONWorker::FixString(const json_string & value_t, const internalJSONNode * flag, bool which){ + #define setflag(x) doflag(flag, which, x) +#else + json_string JSONWorker::FixString(const json_string & value_t, bool & flag){ + #define setflag(x) flag = x +#endif + /* + Do things like unescaping + */ + setflag(false); + json_string res; + res.reserve(value_t.length()); //since it goes one character at a time, want to reserve it first so that it doens't have to reallocating + const json_char * p = value_t.c_str(); + while(*p){ + switch (*p){ + case JSON_TEXT('\\'): + setflag(true); + SpecialChar(++p, res); + break; + default: + res += *p; + break; + } + ++p; + } + return res; +} + +#ifdef JSON_UNICODE + json_string JSONWorker::toSurrogatePair(unsigned json_char C){ + JSON_ASSERT(sizeof(unsigned int) == 4, JSON_TEXT("size of unsigned int is not 32-bit")); + JSON_ASSERT(sizeof(unsigned short) == 2, JSON_TEXT("size of unsigned short is not 16-bit")); + JSON_ASSERT(sizeof(unsigned json_char) == 4, JSON_TEXT("json_char is not 32-bit")); + //Compute the high surrogate + const unsigned int U = (C >> 16) & 31; + unsigned short HiSurrogate = 0xD800 | (((unsigned short)U - 1) << 6) | ((unsigned short)C) >> 10; + + //compute the low surrogate + unsigned short LoSurrogate = (unsigned short) (0xDC00 | ((unsigned short)C) & 1023); + + json_string res; + res += toUTF8(HiSurrogate); + res += toUTF8(LoSurrogate); + return res; + } +#endif + +json_string JSONWorker::toUTF8(unsigned json_char p){ + #ifdef JSON_UNICODE + if (p > 0xFFFF) return toSurrogatePair(p); + #endif + json_string res(JSON_TEXT("\\u")); + #ifdef JSON_UNICODE + unsigned json_char hihi = ((p & 0xF000) >> 12) + 48; + if (hihi > 57) hihi += 7; //A-F don't immediately follow 0-9, so have to further adjust those + unsigned json_char hilo = ((p & 0x0F00) >> 8) + 48; + if (hilo > 57) hilo += 7; //A-F don't immediately follow 0-9, so have to further adjust those + res += hihi; + res += hilo; + unsigned json_char hi = ((p & 0x00F0) >> 4) + 48; + #else + res += JSON_TEXT("00"); + unsigned json_char hi = (p >> 4) + 48; + #endif + //convert the character to be escaped into two digits between 0 and 15 + if (hi > 57) hi += 7; //A-F don't immediately follow 0-9, so have to further adjust those + unsigned json_char lo = (p & 0x000F) + 48; + if (lo > 57) lo += 7; //A-F don't immediately follow 0-9, so have to further adjust those + res += hi; + res += lo; + return res; +} + +json_string JSONWorker::UnfixString(const json_string & value_t, bool flag){ + if (!flag) return value_t; + /* + Re-escapes a json_string so that it can be written out into a JSON file + */ + json_string res; + res.reserve(value_t.length()); //since it goes one character at a time, want to reserve it first so that it doens't have to reallocating + const json_char * p = value_t.c_str(); + while(*p){ + switch(*p){ + case JSON_TEXT('\"'): //quote character + res += JSON_TEXT("\\\""); + break; + case JSON_TEXT('\t'): //tab character + res += JSON_TEXT("\\t"); + break; + case JSON_TEXT('\n'): //newline character + res += JSON_TEXT("\\n"); + break; + case JSON_TEXT('\r'): //return character + res += JSON_TEXT("\\r"); + break; + case JSON_TEXT('\\'): //backslash + res += JSON_TEXT("\\\\"); + break; + case JSON_TEXT('/'): //forward slash + res += JSON_TEXT("\\/"); + break; + case JSON_TEXT('\b'): //backspace + res += JSON_TEXT("\\b"); + break; + case JSON_TEXT('\f'): //formfeed + res += JSON_TEXT("\\f"); + break; + case JSON_TEXT('\v'): //vertical tab + res += JSON_TEXT("\\v"); + break; + case JSON_TEXT('\''): //apostrophe + res += JSON_TEXT("\\\'"); + break; + default: + /*if (((unsigned json_char)(*p) < 32) || ((unsigned json_char)(*p) > 126)) { + //res += toUTF8((unsigned json_char)(*p)); + } else*/ { + res += *p; + } + break; + } + ++p; + } + return res; +} + + +//Create a childnode +#ifdef JSON_COMMENTS + #define ARRAY_PARAM bool array //Just to supress warnings +#else + #define ARRAY_PARAM bool +#endif +inline void JSONWorker::NewNode(const internalJSONNode * parent, const json_string & name, const json_string & value, ARRAY_PARAM){ + #ifdef JSON_COMMENTS + const json_char * runner = (array) ? value.c_str() : name.c_str(); + json_string _comment; + if (*runner == '\5') { //multiple comments will be consolidated into one + newcomment: + while(*(++runner) != '\5') { + JSON_ASSERT(*runner, JSON_TEXT("Removing white space failed")); + _comment += *runner; + } + if (*(++runner) == '\5') { //step past the trailing tag + _comment += '\n'; + goto newcomment; + } + } + internalJSONNode * myinternal; + if (array){ + myinternal = internalJSONNode::newInternal(name, runner); + } else { + myinternal = internalJSONNode::newInternal(++runner, value); + } + JSONNode * child = JSONNode::newJSONNode(myinternal); + child -> set_comment(_comment); + const_cast<internalJSONNode*>(parent) -> Children.push_back(child); //attach it to the parent node + #else + const_cast<internalJSONNode*>(parent) -> Children.push_back(JSONNode::newJSONNode(internalJSONNode::newInternal(name.c_str() + 1, value))); //attach it to the parent node + #endif +} + +//Create a subarray +void JSONWorker::DoArray(const internalJSONNode * parent, const json_string & value_t){ + /* + This takes an array and creates nodes out of them + */ + JSON_ASSERT(!value_t.empty(), JSON_TEXT("DoArray is empty")); + JSON_ASSERT_SAFE(value_t[0] == JSON_TEXT('['), JSON_TEXT("DoArray is not an array"), parent -> Nullify(); return;); + const size_t len = value_t.length(); + if (len <= 2) return; // just a [] (blank array) + + //Not sure what's in the array, so we have to use commas + size_t starting = 1; //ignore the [ + size_t ending = FindNextRelevant(JSON_TEXT(','), value_t, 1); + while (ending != json_string::npos){ + #ifdef JSON_SAFE + json_string newValue = value_t.substr(starting, ending - starting); + JSON_ASSERT_SAFE(FindNextRelevant(JSON_TEXT(':'), newValue, 0) == json_string::npos, JSON_TEXT("Key/Value pairs are not allowed in arrays"), parent -> Nullify(); return;); + NewNode(parent, JSON_TEXT(""), newValue, true); + #else + NewNode(parent, JSON_TEXT(""), value_t.substr(starting, ending - starting), true); + #endif + starting = ending + 1; + ending = FindNextRelevant(JSON_TEXT(','), value_t, starting); + } + //since the last one will not find the comma, we have to add it here, but ignore the final ] + + #ifdef JSON_SAFE + json_string newValue = value_t.substr(starting, len - starting - 1); + JSON_ASSERT_SAFE(FindNextRelevant(JSON_TEXT(':'), newValue, 0) == json_string::npos, JSON_TEXT("Key/Value pairs are not allowed in arrays"), parent -> Nullify(); return;); + NewNode(parent, JSON_TEXT(""), newValue, true); + #else + NewNode(parent, JSON_TEXT(""), value_t.substr(starting, len - starting - 1), true); + #endif +} + + +//Create all child nodes +void JSONWorker::DoNode(const internalJSONNode * parent, const json_string & value_t){ + /* + This take a node and creates its members and such + */ + JSON_ASSERT(!value_t.empty(), JSON_TEXT("DoNode is empty")); + JSON_ASSERT_SAFE(value_t[0] == JSON_TEXT('{'), JSON_TEXT("DoNode is not an node"), parent -> Nullify(); return;); + const size_t len = value_t.length(); + if (len <= 2) return; // just a {} (blank node) + + size_t name_starting = 1; //ignore the { + size_t name_ending = FindNextRelevant(JSON_TEXT(':'), value_t, 1); //find where the name ends + JSON_ASSERT_SAFE(name_ending != json_string::npos, JSON_TEXT("Missing :"), parent -> Nullify(); return;); + json_string name = value_t.substr(name_starting, name_ending - 2); //pull the name out + size_t value_ending = FindNextRelevant(JSON_TEXT(','), value_t, name_ending); //find the end of the value + while (value_ending != json_string::npos){ + NewNode(parent, name, value_t.substr(name_ending + 1, value_ending - name_ending - 1), false); + name_starting = value_ending + 1; + name_ending = FindNextRelevant(JSON_TEXT(':'), value_t, name_starting); + JSON_ASSERT_SAFE(name_ending != json_string::npos, JSON_TEXT("Missing :"), parent -> Nullify(); return;); + name = value_t.substr(name_starting, name_ending - name_starting - 1); + value_ending = FindNextRelevant(JSON_TEXT(','), value_t, name_ending); + } + //since the last one will not find the comma, we have to add it here + NewNode(parent, name, value_t.substr(name_ending + 1, len - name_ending - 2), false); +} diff --git a/src/modules/json/JSONWorker.h b/src/modules/json/JSONWorker.h new file mode 100644 index 0000000000..373660743c --- /dev/null +++ b/src/modules/json/JSONWorker.h @@ -0,0 +1,46 @@ +#ifndef JSON_WORKER_H +#define JSON_WORKER_H + +#include "JSONNode.h" + +class JSONWorker { +public: + static JSONNode parse(const json_string & json); + #ifdef JSON_VALIDATE + static JSONNode validate(const json_string & json); + #endif + #if defined JSON_DEBUG || defined JSON_SAFE + static json_char * RemoveWhiteSpace(const json_string & value_t, json_char & last); + #else + static json_char * RemoveWhiteSpace(const json_string & value_t); + #endif + static json_string RemoveWhiteSpaceAndComments(const json_string & value_t); + + static void DoArray(const internalJSONNode * parent, const json_string & value_t); + static void DoNode(const internalJSONNode * parent, const json_string & value_t); + + #ifdef JSON_LESS_MEMORY + #define NAME_ENCODED this, true + #define STRING_ENCODED this, false + static json_string FixString(const json_string & value_t, const internalJSONNode * flag, bool which); + #else + #define NAME_ENCODED _name_encoded + #define STRING_ENCODED _string_encoded + static json_string FixString(const json_string & value_t, bool & flag); + #endif + static json_string UnfixString(const json_string & value_t, bool flag); +JSON_PRIVATE + static json_char Hex(const json_char * & pos); + static unsigned json_char UTF8(const json_char * & pos); + static json_char* UTF8_2(const json_char * & pos); + static json_string toUTF8(unsigned json_char p); + #ifdef JSON_UNICODE + static json_string UTF(const json_char * & pos); + static json_string toSurrogatePair(unsigned json_char pos); + #endif + static void SpecialChar(const json_char * & pos, json_string & res); + static size_t FindNextRelevant(json_char ch, const json_string & value_t, const size_t pos); + static void NewNode(const internalJSONNode * parent, const json_string & name, const json_string & value, bool array); +}; + +#endif diff --git a/src/modules/json/JSONWriter.cpp b/src/modules/json/JSONWriter.cpp new file mode 100644 index 0000000000..f23798502b --- /dev/null +++ b/src/modules/json/JSONWriter.cpp @@ -0,0 +1,173 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+
+#include "JSONNode.h" +#ifdef JSON_WRITER +#include "JSONWorker.h" + +const static json_string WRITER_EMPTY; +#ifndef JSON_NEWLINE + const static json_string NEW_LINE(JSON_TEXT("\n")); +#else + const static json_string NEW_LINE(JSON_TEXT(JSON_NEWLINE)); +#endif + +#ifdef JSON_INDENT + const static json_string INDENT(JSON_TEXT(JSON_INDENT)); + + inline json_string makeIndent(unsigned int amount){ + if (amount == 0xFFFFFFFF) return WRITER_EMPTY; + json_string result; + result.reserve(amount); + for(unsigned int i = 0; i < amount; ++i){ + result += INDENT; + } + return result; + } +#else + inline json_string makeIndent(unsigned int amount){ + if (amount == 0xFFFFFFFF) return WRITER_EMPTY; + return json_string(amount, JSON_TEXT('\t')); + } +#endif + +json_string internalJSONNode::WriteName(bool formatted, bool arrayChild) const { + if (arrayChild){ + return WRITER_EMPTY ; + } else { + return JSON_TEXT("\"") + JSONWorker::UnfixString(_name, _name_encoded) + ((formatted) ? JSON_TEXT("\" : ") : JSON_TEXT("\":")); + } +} + +json_string internalJSONNode::WriteChildren(unsigned int indent){ + //Iterate through the children and write them + if (Children.empty()) return WRITER_EMPTY; + + json_string indent_plus_one; + json_string indent_this; + json_string res; + //handle whether or not it's formatted JSON + if (indent != 0xFFFFFFFF){ //it's formatted, make the indentation strings + indent_this = NEW_LINE + makeIndent(indent); + indent_plus_one = NEW_LINE + makeIndent(++indent); + } + //else it's not formatted, leave the indentation strings empty + const size_t size_minus_one = Children.size() - 1; + size_t i = 0; + json_foreach(Children, it){ + res += indent_plus_one + (*it) -> internal -> Write(indent, type() == JSON_ARRAY); + if (i < size_minus_one) res += JSON_TEXT(","); //the last one does not get a comma, but all of the others do + ++i; + } + return res + indent_this; +} + +#ifdef JSON_COMMENTS + #ifdef JSON_WRITE_BASH_COMMENTS + const static json_string SINGLELINE(JSON_TEXT("#")); + #else + const static json_string SINGLELINE(JSON_TEXT("//")); + #endif + + json_string internalJSONNode::WriteComment(unsigned int indent) const { + if (indent == 0xFFFFFFFF) return WRITER_EMPTY; + if (_comment.empty()) return WRITER_EMPTY; + size_t pos = _comment.find(JSON_TEXT('\n')); + if (pos == json_string::npos){ //Single line comment + return NEW_LINE + makeIndent(indent) + SINGLELINE + _comment + NEW_LINE + makeIndent(indent); + } + + /* + Multiline comments + */ + #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS) + json_string result(NEW_LINE + makeIndent(indent)); + #else + json_string result(NEW_LINE + makeIndent(indent) + JSON_TEXT("/*") + NEW_LINE + makeIndent(indent + 1)); + #endif + size_t old = 0; + while(pos != json_string::npos){ + if (pos && _comment[pos - 1] == JSON_TEXT('\r')) --pos; + #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS) + result += SINGLELINE; + #endif + result += _comment.substr(old, pos - old) + NEW_LINE; + #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS) + result += makeIndent(indent); + #else + result += makeIndent(indent + 1); + #endif + old = (_comment[pos] == JSON_TEXT('\r')) ? pos + 2 : pos + 1; + pos = _comment.find(JSON_TEXT('\n'), old); + } + #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS) + result += SINGLELINE; + #endif + result += _comment.substr(old, pos - old) + NEW_LINE + makeIndent(indent); + #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS) + return result; + #else + return result + JSON_TEXT("*/") + NEW_LINE + makeIndent(indent); + #endif + } +#else + json_string internalJSONNode::WriteComment(unsigned int) const { + return WRITER_EMPTY; + } +#endif + +json_string internalJSONNode::Write(unsigned int indent, bool arrayChild){ + const bool formatted = indent != 0xFFFFFFFF; + + #ifndef JSON_PREPARSE + if (!(formatted || fetched)) { //It's not formatted or fetched, just do a raw dump + return WriteComment(indent) + WriteName(false, arrayChild) + _string; + } + #endif + + //It's either formatted or fetched + switch (type()) { + case JSON_NODE: //got members, write the members + Fetch(); + return WriteComment(indent) + WriteName(formatted, arrayChild) + JSON_TEXT("{") + WriteChildren(indent) + JSON_TEXT("}"); + case JSON_ARRAY: //write out the child nodes int he array + Fetch(); + return WriteComment(indent) + WriteName(formatted, arrayChild) + JSON_TEXT("[") + WriteChildren(indent) + JSON_TEXT("]"); + case JSON_NUMBER: //write out a literal, without quotes + case JSON_NULL: + case JSON_BOOL: + return WriteComment(indent) + WriteName(formatted, arrayChild) + _string; + } + + JSON_ASSERT_SAFE(type() == JSON_STRING, JSON_TEXT("Writing an unknown JSON node type"), return JSON_TEXT("");); + //If it go here, then it's a json_string + #ifndef JSON_PREPARSE + if (fetched) return WriteComment(indent) + WriteName(formatted, arrayChild) + JSON_TEXT("\"") + JSONWorker::UnfixString(_string, _string_encoded) + JSON_TEXT("\""); //It's already been fetched, meaning that it's unescaped + return WriteComment(indent) + WriteName(formatted, arrayChild) + _string; //it hasn't yet been fetched, so it's already unescaped, just do a dump + #else + return WriteComment(indent) + WriteName(formatted, arrayChild) + JSON_TEXT("\"") + JSONWorker::UnfixString(_string, _string_encoded) + JSON_TEXT("\""); + #endif +} +#endif diff --git a/src/modules/json/JSON_Base64.cpp b/src/modules/json/JSON_Base64.cpp new file mode 100644 index 0000000000..4759b112bb --- /dev/null +++ b/src/modules/json/JSON_Base64.cpp @@ -0,0 +1,136 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+
+#include "JSON_Base64.h" +#include "JSONDefs.h" + +#ifdef JSON_BINARY //if this is not needed, don't waste space compiling it + +static const json_char * chars64 = JSON_TEXT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); +#ifdef JSON_UNICODE + static std::string BASE64_EMPTY_STRING; +#else + #define BASE64_EMPTY_STRING EMPTY_STRING +#endif + +json_string JSONBase64::json_encode64(const unsigned char * binary, size_t bytes){ + size_t misaligned = bytes % 3; + size_t aligned = (bytes - misaligned) / 3; //divide by three once and inc is faster than add 3 each time + json_string result; + result.reserve((size_t)(((float)bytes) * 1.37f) + 4); + + //do all of the ones that are 3 byte aligned + for (size_t i = 0; i < aligned; ++i){ + result += chars64[(binary[0] & 0xFC) >> 2]; + result += chars64[((binary[0] & 0x03) << 4) + ((binary[1] & 0xF0) >> 4)]; + result += chars64[((binary[1] & 0x0F) << 2) + ((binary[2] & 0xC0) >> 6)]; + result += chars64[binary[2] & 0x3F]; + binary += 3; + } + + if (misaligned){ + //copy the rest into a temporary buffer + unsigned char temp[3]; + for (unsigned int i = 0; i < misaligned; ++i){ + temp[i] = *binary++; + } + for (unsigned int i = (unsigned int)misaligned; i < 3; ++i){ + temp[i] = '\0'; + } + + //now do the final three bytes + result += chars64[(temp[0] & 0xFC) >> 2]; + result += chars64[((temp[0] & 0x03) << 4) + ((temp[1] & 0xF0) >> 4)]; + if (misaligned == 2){ + result += chars64[((temp[1] & 0x0F) << 2) + ((temp[2] & 0xC0) >> 6)]; + result += JSON_TEXT('='); + } else { + result += JSON_TEXT("=="); + } + } + JSON_ASSERT((size_t)(((float)bytes) * 1.37f) + 4 >= result.length(), JSON_TEXT("Didn't reserve enough space for a one-time go")); + return result; +} + +inline json_char toBinary(json_char c){ + if (c == JSON_TEXT('+')) { + return JSON_TEXT('>'); + } else if (c == JSON_TEXT('/')) { + return JSON_TEXT('?'); + } else if (c < JSON_TEXT(':')) { + return c + JSON_TEXT('\x04'); + } else if (c < JSON_TEXT('[')) { + return c - JSON_TEXT('\x41'); + } + return c - 71; +} + +/* + Must be a std::string because it's binary, and chars must be 1 byte + */ +std::string JSONBase64::json_decode64(const json_string & encoded){ + const size_t length = encoded.length(); + #if defined JSON_DEBUG || defined JSON_SAFE + size_t pos = encoded.find_first_not_of(chars64); + if (pos != json_string::npos){ + JSON_ASSERT_SAFE(encoded[pos] == JSON_TEXT('='), JSON_TEXT("Not Base64"), return BASE64_EMPTY_STRING;); + if (pos != length - 1){ + JSON_ASSERT_SAFE(pos == length - 2, JSON_TEXT("Not Base64"), return BASE64_EMPTY_STRING;); + JSON_ASSERT_SAFE(encoded[pos + 1] == JSON_TEXT('='), JSON_TEXT("Not Base64"), return BASE64_EMPTY_STRING;); + } + } + #endif + const json_char * runner = encoded.c_str(); + size_t aligned = length / 4; //don't do the last ones as they might be = padding + std::string result; + if (aligned){ + --aligned; + result.reserve((size_t)((float)length / 1.37) + 1); + + //first do the ones that can not have any padding + for (unsigned int i = 0; i < aligned; ++i){ + const json_char second = toBinary(runner[1]); + const json_char third = toBinary(runner[2]); + result += (toBinary(runner[0]) << 2) + ((second & 0x30) >> 4); + result += ((second & 0xf) << 4) + ((third & 0x3c) >> 2); + result += ((third & 0x3) << 6) + toBinary(runner[3]); + runner += 4; + } + + //now do the ones that might have padding, the first two characters can not be padding, so do them quickly + const char second = toBinary(runner[1]); + result += (toBinary(runner[0]) << 2) + ((second & 0x30) >> 4); + if (runner[2] != '=') { //not two = pads + const char third = toBinary(runner[2]); + result += ((second & 0xf) << 4) + ((third & 0x3c) >> 2); + if (runner[3] != '=') { //no padding + result += ((third & 0x3) << 6) + toBinary(runner[3]); + } + } + } + return result; +} + +#endif diff --git a/src/modules/json/JSON_Base64.h b/src/modules/json/JSON_Base64.h new file mode 100644 index 0000000000..1e2d76baa8 --- /dev/null +++ b/src/modules/json/JSON_Base64.h @@ -0,0 +1,14 @@ +#ifndef JSON_BASE64_H +#define JSON_BASE64_H + +#include "JSONDebug.h" +#ifdef JSON_BINARY //if this is not needed, don't waste space compiling it + +class JSONBase64 { +public: + static json_string json_encode64(const unsigned char * binary, size_t bytes); + static std::string json_decode64(const json_string & encoded); +}; + +#endif +#endif diff --git a/src/modules/json/NumberToString.h b/src/modules/json/NumberToString.h new file mode 100644 index 0000000000..a5e93d4302 --- /dev/null +++ b/src/modules/json/NumberToString.h @@ -0,0 +1,104 @@ +#ifndef NUMBERTOSTRING_H +#define NUMBERTOSTRING_H + +#include "JSONDebug.h" +#include "JSONMemory.h" +#include <cstdio> + +static unsigned int getlen(unsigned int size){ + switch (size){ + case 1: + return 5; //3 digits for the number, plus null terminator and negation + case 2: + return 7; //5 digits for the number, plus null terminator and negation + case 4: + return 12; //10 digits for the number, plus null terminator and negation + case 8: + return 22; //20 digits for the number, plus null terminator and negation + } + JSON_ASSERT(size == 16, JSON_TEXT("template is not recognized 2^x in size")); + return 41; //39 digits for the number, plus null terminator and negation +} + +class NumberToString { +public: + template<typename T> + static json_string _itoa(T val, unsigned int size){ + long value = (long)val; + const unsigned int digits = getlen(size); + json_auto<json_char> result(digits); + result.ptr[digits - 1] = JSON_TEXT('\0'); //null terminator + json_char * runner = &result.ptr[digits - 2]; + bool negative; + + //first thing, check if it's negative, if so, make it positive + if (value < 0){ + value = -value; + negative = true; + } else { + negative = false; + } + + //create the string + do { + *runner-- = (json_char)(value % 10) + JSON_TEXT('0'); + } while(value /= 10); + + //if it's negative, add the negation + json_string res; + if (negative){ + *runner = JSON_TEXT('-'); + res = runner; + } else { + res = runner + 1; + } + return res; + } + + #ifndef JSON_LIBRARY + template<typename T> + static json_string _uitoa(T val, unsigned int size){ + unsigned long value = val; + const unsigned int digits = getlen(size) - 1; //minus one because no '-' char + json_auto<json_char> result(digits); + result.ptr[digits - 1] = JSON_TEXT('\0'); //null terminator + json_char * runner = &result.ptr[digits - 2]; + + //create the string + do { + *runner-- = (json_char)(value % 10) + JSON_TEXT('0'); + } while(value /= 10); + + json_string res = runner + 1; + return res; + } + #endif + + template<typename T> + static json_string _ftoa(T value){ + json_char result[64]; + #ifdef JSON_UNICODE + swprintf(result, 63, L"%f", value); + #else + snprintf(result, 63, "%f", value); + #endif + //strip the trailing zeros + for(json_char * pos = &result[0]; *pos; ++pos){ + if (*pos == '.') { //only care about after the decimal + for(json_char * runner = pos + 1; *runner; ++runner){ + if (*runner != JSON_TEXT('0')) pos = runner + 1; + } + *pos = JSON_TEXT('\0'); + break; + } + } + return result; + } + + static inline bool areEqual(const json_number & one, const json_number & two){ + const json_number temp = one - two; + return (temp > 0.0) ? temp < 0.00001 : temp > -0.00001; + } +}; + +#endif diff --git a/src/modules/json/internalJSONNode.cpp b/src/modules/json/internalJSONNode.cpp new file mode 100644 index 0000000000..26e203b284 --- /dev/null +++ b/src/modules/json/internalJSONNode.cpp @@ -0,0 +1,572 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+#include <m_json.h>
+
+#include "libJSON.h"
+#include "internalJSONNode.h" +#include "NumberToString.h" //So that I can convert numbers into strings +#include "JSONNode.h" //To fill in the foreward declaration +#include "JSONWorker.h" //For fetching and parsing and such + +/* + The point of these constants is for faster assigning, if I + were to constantly assign to a string literal, there would be + lots of copies, but since strings are copy on write, this assignment + is much faster +*/ +static const json_string CONST_TRUE(JSON_TEXT("true")); +static const json_string CONST_FALSE(JSON_TEXT("false")); +static const json_string CONST_NULL(JSON_TEXT("null")); + +#ifdef JSON_UNIT_TEST + void internalJSONNode::incinternalAllocCount(void){ JSONNode::incinternalAllocCount(); } + void internalJSONNode::decinternalAllocCount(void){ JSONNode::decinternalAllocCount(); } +#endif + +internalJSONNode::internalJSONNode(const internalJSONNode & orig) : + _type(orig._type), _name(orig._name), _name_encoded(orig._name_encoded), Children(), + _string(orig._string), _string_encoded(orig._string_encoded), _value(orig._value) + initializeRefCount(1) + initializeFetch(orig.fetched) + initializeMutex(0) + initializeComment(orig._comment) + initializeValid(orig.isValid){ + + + incinternalAllocCount(); + #ifdef JSON_MUTEX_CALLBACKS + _set_mutex(orig.mylock, false); + #endif + if (!orig.Children.empty()) { + Children.reserve(orig.Children.size()); + json_foreach(orig.Children, myrunner){ + Children.push_back(JSONNode::newJSONNode((*myrunner) -> duplicate())); + } + } +} + +#ifdef JSON_PREPARSE + #define SetFetchedFalseOrDo(code) code +#else + #define SetFetchedFalseOrDo(code) SetFetched(false) +#endif + +#ifdef JSON_VALIDATE + #define NOTVALID false +#else + #define NOTVALID +#endif + +//this one is specialized because the root can only be array or node +internalJSONNode::internalJSONNode(const json_string & unparsed) : _type(), _name(),_name_encoded(false), _string(unparsed), _string_encoded(), _value(), Children() + initializeMutex(0) + initializeRefCount(1) + initializeFetch(false) + initializeComment(0) + initializeValid(0){ + + incinternalAllocCount(); + switch (unparsed[0]) { + case JSON_TEXT('{'): //node + _type = JSON_NODE; + #ifdef JSON_PREPARSE + FetchNode(); + #endif + break; + case JSON_TEXT('['): //array + _type = JSON_ARRAY; + #ifdef JSON_PREPARSE + FetchArray(); + #endif + break; + default: + JSON_FAIL_SAFE(JSON_TEXT("root not starting with either { or ["), Nullify(NOTVALID);); + break; + } +} + +internalJSONNode::internalJSONNode(const json_string & name_t, const json_string & value_t) : _type(), _name_encoded(), _name(JSONWorker::FixString(name_t, NAME_ENCODED)), _string(), _string_encoded(), _value(), Children() + initializeMutex(0) + initializeRefCount(1) + initializeFetch(0) + initializeComment(0) + initializeValid(0){ + + incinternalAllocCount(); + + if (value_t.empty()) { + _type = JSON_NULL; + #ifdef JSON_VALIDATE + isValid = true; + #endif + SetFetched(true); + return; + } + + _string = value_t; + const json_char firstchar = value_t[0]; + #if defined JSON_DEBUG || defined JSON_SAFE + const json_char lastchar = value_t[value_t.length() - 1]; + #endif + + switch (firstchar){ + case JSON_TEXT('\"'): //a json_string literal, still escaped and with leading and trailing quotes + JSON_ASSERT_SAFE(lastchar == JSON_TEXT('\"'), JSON_TEXT("Unterminated quote"), Nullify(NOTVALID); return;); + _type = JSON_STRING; + SetFetchedFalseOrDo(FetchString()); + break; + case JSON_TEXT('{'): //a child node, or set of children + JSON_ASSERT_SAFE(lastchar == JSON_TEXT('}'), JSON_TEXT("Missing }"), Nullify(NOTVALID); return;); + _type = JSON_NODE; + SetFetchedFalseOrDo(FetchNode()); + break; + case JSON_TEXT('['): //an array + JSON_ASSERT_SAFE(lastchar == JSON_TEXT(']'), JSON_TEXT("Missing ]"), Nullify(NOTVALID); return;); + _type = JSON_ARRAY; + SetFetchedFalseOrDo(FetchArray()); + break; + case JSON_TEXT('t'): + JSON_ASSERT_SAFE(value_t == JSON_TEXT("true"), json_string(json_string(JSON_TEXT("unknown JSON literal: ")) + value_t).c_str(), Nullify(NOTVALID); return;); + _value._bool = true; + _type = JSON_BOOL; + SetFetched(true); + break; + case JSON_TEXT('f'): + JSON_ASSERT_SAFE(value_t == JSON_TEXT("false"), json_string(json_string(JSON_TEXT("unknown JSON literal: ")) + value_t).c_str(), Nullify(NOTVALID); return;); + _value._bool = false; + _type = JSON_BOOL; + SetFetched(true); + break; + case JSON_TEXT('n'): + JSON_ASSERT_SAFE(value_t == JSON_TEXT("null"), json_string(json_string(JSON_TEXT("unknown JSON literal: ")) + value_t).c_str(), Nullify(NOTVALID); return;); + _type = JSON_NULL; + SetFetched(true); + #ifdef JSON_VALIDATE + isValid = true; + #endif + break; + default: + JSON_ASSERT_SAFE(value_t.find_first_not_of(JSON_TEXT("0123456789.e+-")) == json_string::npos, json_string(json_string(JSON_TEXT("unknown JSON literal: ")) + value_t).c_str(), Nullify(NOTVALID); return;); + _type = JSON_NUMBER; + SetFetchedFalseOrDo(FetchNumber()); + break; + } +} + +internalJSONNode::~internalJSONNode(void){ + decinternalAllocCount(); + #ifdef JSON_MUTEX_CALLBACKS + _unset_mutex(); + #endif + //DO NOT delete the children! It automatically gets removed +} + +void internalJSONNode::FetchString(void) const { + JSON_ASSERT_SAFE(!_string.empty(), JSON_TEXT("JSON json_string type is empty?"), Nullify(NOTVALID); return;); + JSON_ASSERT_SAFE(_string[0] == JSON_TEXT('\"'), JSON_TEXT("JSON json_string type doesn't start with a quotation?"), Nullify(NOTVALID); return;); + JSON_ASSERT_SAFE(_string[_string.length() - 1] == JSON_TEXT('\"'), JSON_TEXT("JSON json_string type doesn't end with a quotation?"), Nullify(NOTVALID); return;); + _string = JSONWorker::FixString(_string.substr(1, _string.length() - 2), STRING_ENCODED); +} + +void internalJSONNode::FetchNode(void) const { + JSON_ASSERT_SAFE(!_string.empty(), JSON_TEXT("JSON node type is empty?"), Nullify(NOTVALID); return;); + JSON_ASSERT_SAFE(_string[0] == JSON_TEXT('{'), JSON_TEXT("JSON node type doesn't start with a bracket?"), Nullify(NOTVALID); return;); + JSON_ASSERT_SAFE(_string[_string.length() - 1] == JSON_TEXT('}'), JSON_TEXT("JSON node type doesn't end with a bracket?"), Nullify(NOTVALID); return;); + JSONWorker::DoNode(this, _string); + clearString(_string); +} + +void internalJSONNode::FetchArray(void) const { + JSON_ASSERT_SAFE(!_string.empty(), JSON_TEXT("JSON node type is empty?"), Nullify(NOTVALID); return;); + JSON_ASSERT_SAFE(_string[0] == JSON_TEXT('['), JSON_TEXT("JSON node type doesn't start with a square bracket?"), Nullify(NOTVALID); return;); + JSON_ASSERT_SAFE(_string[_string.length() - 1] == JSON_TEXT(']'), JSON_TEXT("JSON node type doesn't end with a square bracket?"), Nullify(NOTVALID); return;); + JSONWorker::DoArray(this, _string); + clearString(_string); +} + +void internalJSONNode::FetchNumber(void) const { + #ifdef JSON_UNICODE + { + const size_t len = _string.length(); + json_auto<char> temp(len + 1); + wcstombs(temp.ptr, _string.c_str(), len); + temp.ptr[len] = '\0'; + _value._number = (json_number)atof(temp.ptr); + } + #else + _value._number = (json_number)atof(_string.c_str()); + #endif +} + +#ifndef JSON_PREPARSE + void internalJSONNode::Fetch(void) const { + if (fetched) return; + switch (type()) { + case JSON_STRING: + FetchString(); + break; + case JSON_NODE: + FetchNode(); + break; + case JSON_ARRAY: + FetchArray(); + break; + case JSON_NUMBER: + FetchNumber(); + break; + #if defined JSON_DEBUG || defined JSON_SAFE + default: + JSON_FAIL(JSON_TEXT("Fetching an unknown type")); + Nullify(NOTVALID); + #endif + } + fetched = true; + } +#endif + +void internalJSONNode::Set(const json_string & val){ + _type = JSON_STRING; + _string = val; + _string_encoded = true; + SetFetched(true); +} + + +#ifdef JSON_LIBRARY + void internalJSONNode::Set(long val){ + _type = JSON_NUMBER; + _value._number = (json_number)val; + _string = NumberToString::_itoa<long>(val, sizeof(long)); + SetFetched(true); + } + + void internalJSONNode::Set(json_number val){ + _type = JSON_NUMBER; + _value._number = val; + _string = NumberToString::_ftoa<json_number>(val); + SetFetched(true); + } +#else + #define SET(converter, type)\ + void internalJSONNode::Set(type val){\ + _type = JSON_NUMBER;\ + _value._number = (json_number)val;\ + _string = NumberToString::converter<type>(val, sizeof(type));\ + SetFetched(true);\ + } + #define SET_INTEGER(type) SET(_itoa, type) SET(_uitoa, unsigned type) + #define SET_FLOAT(type) \ + void internalJSONNode::Set(type val){\ + _type = JSON_NUMBER;\ + _value._number = (json_number)val;\ + _string = NumberToString::_ftoa<type>(val);\ + SetFetched(true);\ + } + + SET_INTEGER(char) + SET_INTEGER(short) + SET_INTEGER(int) + SET_INTEGER(long) + #ifndef JSON_ISO_STRICT + SET_INTEGER(long long) + #endif + + SET_FLOAT(float) + SET_FLOAT(double) +#endif + +void internalJSONNode::Set(bool val){ + _type = JSON_BOOL; + _value._bool = val; + _string = val ? CONST_TRUE : CONST_FALSE; + SetFetched(true); +} + +bool internalJSONNode::IsEqualTo(const internalJSONNode * val) const { + #ifdef JSON_REF_COUNT + if (this == val) return true; //reference counting the same internal object, so they must be equal + #endif + if (type() != val -> type()) return false; //aren't even same type + if (_name != val -> _name) return false; //names aren't the same + if (type() == JSON_NULL) return true; //both null, can't be different + #ifndef JSON_PREPARSE + Fetch(); + val -> Fetch(); + #endif + switch (type()) { + case JSON_STRING: + return val -> _string == _string; + case JSON_NUMBER: + return NumberToString::areEqual(val -> _value._number, _value._number); + case JSON_BOOL: + return val -> _value._bool == _value._bool; + }; + + JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("Checking for equality, not sure what type")); + if (Children.size() != val -> Children.size()) return false; //if they arne't he same size then they certainly aren't equal + + //make sure each children is the same + JSONNode ** valrunner = val -> Children.begin(); + json_foreach(Children, myrunner){ + JSON_ASSERT(*myrunner, JSON_TEXT("a null pointer within the children")); + JSON_ASSERT(*valrunner, JSON_TEXT("a null pointer within the children")); + JSON_ASSERT(valrunner != val -> Children.end(), JSON_TEXT("at the end of other one's children, but they're the same size?")); + if (**myrunner != **valrunner) return false; + ++valrunner; + } + return true; +} + +#ifdef JSON_VALIDATE +void internalJSONNode::Nullify(bool validation) const { + isValid = validation; +#else +void internalJSONNode::Nullify(void) const { +#endif + _type = JSON_NULL; + _string = CONST_NULL; + SetFetched(true); +} + +#ifdef JSON_MUTEX_CALLBACKS + #define JSON_MUTEX_COPY ,mylock +#else + #define JSON_MUTEX_COPY +#endif + +#ifdef JSON_LIBRARY +void internalJSONNode::push_back(JSONNode * node){ +#else +void internalJSONNode::push_back(const JSONNode & node){ +#endif + JSON_ASSERT(type() == JSON_ARRAY || type() == JSON_NODE, JSON_TEXT("pushing back to something that is not an array or object")); + #ifdef JSON_LIBRARY + #ifdef JSON_MUTEX_CALLBACKS + if (mylock) node -> set_mutex(mylock); + #endif + Children.push_back(node); + #else + Children.push_back(JSONNode::newJSONNode(node JSON_MUTEX_COPY)); + #endif +} + +void internalJSONNode::push_front(const JSONNode & node){ + JSON_ASSERT(type() == JSON_ARRAY || type() == JSON_NODE, JSON_TEXT("pushing front to something that is not an array or object")); + Children.push_front(JSONNode::newJSONNode(node JSON_MUTEX_COPY)); +} + +JSONNode * internalJSONNode::pop_back(json_index_t pos){ + JSONNode * result = Children[pos]; + JSONNode ** temp = Children.begin() + pos; + Children.erase(temp); + return result; +} + +JSONNode * internalJSONNode::pop_back(const json_string & name_t){ + if (JSONNode ** res = at(name_t)) { + JSONNode * result = *res; + Children.erase(res); + return result; + } + return 0; +} + +#ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNode * internalJSONNode::pop_back_nocase(const json_string & name_t){ + if (JSONNode ** res = at_nocase(name_t)) { + JSONNode * result = *res; + Children.erase(res); + return result; + } + return 0; + } +#endif + +JSONNode ** internalJSONNode::at(const json_string & name_t){ + Fetch(); + json_foreach(Children, myrunner){ + JSON_ASSERT(*myrunner, JSON_TEXT("a null pointer within the children")); + if ((*myrunner) -> name() == name_t) return myrunner; + } + return 0; +} + +#ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + bool internalJSONNode::AreEqualNoCase(const json_char * ch_one, const json_char * ch_two){ + while (*ch_one){ //only need to check one, if the other one terminates early, the check will cause it to fail + const json_char c_one = *ch_one; + const json_char c_two = *ch_two; + if (c_one != c_two){ + if ((c_two > 64) && (c_two < 91)) { //A - Z + if (c_one != (json_char)(c_two + 32)) return false; + } else if ((c_two > 96) && (c_two < 123)) { //a - z + if (c_one != (json_char)(c_two - 32)) return false; + } else { //not a letter, so return false + return false; + } + } + ++ch_one; + ++ch_two; + + } + return *ch_two == '\0'; //this one has to be null terminated too, or else json_string two is longer, hence, not equal + } + + JSONNode ** internalJSONNode::at_nocase(const json_string & name_t){ + Fetch(); + json_foreach(Children, myrunner){ + JSON_ASSERT(*myrunner, JSON_TEXT("a null pointer within the children")); + if (AreEqualNoCase((*myrunner) -> name().c_str(), name_t.c_str())) return myrunner; + } + return 0; + } +#endif + +#ifndef JSON_PREPARSE + void internalJSONNode::preparse(void){ + Fetch(); + json_foreach(Children, myrunner){ + (*myrunner) -> preparse(); + } + } +#endif + +#ifdef JSON_VALIDATE + bool internalJSONNode::validate(void){ + json_foreach(Children, myrunner){ + if ((*myrunner) -> type() != JSON_NULL){ + #ifndef JSON_PREPARSE + (*myrunner) -> internal -> Fetch(); + #endif + if ((*myrunner) -> type() == JSON_NULL) return false; + } else if (!((*myrunner) -> internal -> isValid)) { + JSON_FAIL(_name + JSON_TEXT(" is null and not valid")); + return false; + } + } + json_foreach(Children, runner){ + if (!((*runner) -> internal -> validate())) return false; + } + return true; + } +#endif + +#ifdef JSON_DEBUG + #ifndef JSON_LIBRARY + JSONNode internalJSONNode::Dump(size_t & totalbytes) const { + JSONNode dumpage(JSON_NODE); + dumpage.set_name(JSON_TEXT("internalJSONNode")); + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("this"), (long)this))); + + size_t memory = sizeof(internalJSONNode); + memory += _name.capacity() * sizeof(json_char); + memory += _string.capacity() * sizeof(json_char); + memory += Children.capacity() * sizeof(JSONNode*); + #ifdef JSON_COMMENTS + memory += _comment.capacity() * sizeof(json_char); + #endif + totalbytes += memory; + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("bytes used"), memory))); + + + #ifdef JSON_REF_COUNT + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("refcount"), refcount))); + #endif + #ifdef JSON_MUTEX_CALLBACKS + dumpage.push_back(JSON_NEW(DumpMutex())); + #endif + + + #define DUMPCASE(ty)\ + case ty:\ + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("_type"), JSON_TEXT(#ty))));\ + break; + + switch(type()) { + DUMPCASE(JSON_NULL) + DUMPCASE(JSON_STRING) + DUMPCASE(JSON_NUMBER) + DUMPCASE(JSON_BOOL) + DUMPCASE(JSON_ARRAY) + DUMPCASE(JSON_NODE) + default: + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("_type"), JSON_TEXT("Unknown")))); + } + + JSONNode str(JSON_NODE); + str.set_name(JSON_TEXT("_name")); + str.push_back(JSON_NEW(JSONNode(json_string(JSON_TEXT("value")), _name))); + str.push_back(JSON_NEW(JSONNode(JSON_TEXT("length"), _name.length()))); + str.push_back(JSON_NEW(JSONNode(JSON_TEXT("capactiy"), _name.capacity()))); + + #ifdef JSON_LESS_MEMORY + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("_name_encoded"), _type & 0x10))); + #else + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("_name_encoded"), _name_encoded))); + #endif + dumpage.push_back(JSON_NEW(str)); + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("_string_encoded"), _string_encoded))); + str.clear(); + str.set_name(JSON_TEXT("_string")); + str.push_back(JSON_NEW(JSONNode(json_string(JSON_TEXT("value")), _string))); + str.push_back(JSON_NEW(JSONNode(JSON_TEXT("length"), _string.length()))); + str.push_back(JSON_NEW(JSONNode(JSON_TEXT("capactiy"), _string.capacity()))); + dumpage.push_back(JSON_NEW(str)); + + JSONNode unio(JSON_NODE); + unio.set_name(JSON_TEXT("_value")); + unio.push_back(JSON_NEW(JSONNode(JSON_TEXT("_bool"), _value._bool))); + unio.push_back(JSON_NEW(JSONNode(JSON_TEXT("_number"), _value._number))); + dumpage.push_back(JSON_NEW(unio)); + + #ifndef JSON_PREPARSE + dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("fetched"), fetched))); + #endif + + #ifdef JSON_COMMENTS + str.clear(); + str.set_name(JSON_TEXT("_comment")); + str.push_back(JSON_NEW(JSONNode(JSON_TEXT("value"), _comment))); + str.push_back(JSON_NEW(JSONNode(JSON_TEXT("length"), _comment.length()))); + str.push_back(JSON_NEW(JSONNode(JSON_TEXT("capactiy"), _comment.capacity()))); + dumpage.push_back(JSON_NEW(str)); + #endif + + JSONNode arra(JSON_NODE); + arra.set_name(JSON_TEXT("Children")); + arra.push_back(JSON_NEW(JSONNode(JSON_TEXT("size"), Children.size()))); + arra.push_back(JSON_NEW(JSONNode(JSON_TEXT("capacity"), Children.capacity()))); + JSONNode chil(JSON_ARRAY); + chil.set_name(JSON_TEXT("array")); + json_foreach(Children, it){ + chil.push_back(JSON_NEW((*it) -> dump(totalbytes))); + } + arra.push_back(JSON_NEW(chil)); + dumpage.push_back(JSON_NEW(arra)); + + return dumpage; + } + #endif +#endif diff --git a/src/modules/json/internalJSONNode.h b/src/modules/json/internalJSONNode.h new file mode 100644 index 0000000000..9e9f9de983 --- /dev/null +++ b/src/modules/json/internalJSONNode.h @@ -0,0 +1,453 @@ +#ifndef INTERNAL_JSONNODE_H +#define INTERNAL_JSONNODE_H + +#include "JSONDebug.h" +#include "JSONChildren.h" +#include "JSONMemory.h" +#ifdef JSON_DEBUG + #include <climits> //to check int value +#endif + +/* + This class is the work horse of libJSON, it handles all of the + functinality of JSONNode. This object is reference counted for + speed and memory reasons. + + If JSON_REF_COUNT is not on, this internal structure still has an important + purpose, as it can be passed around by JSONNoders that are flagged as temporary +*/ + +class JSONNode; //forward declaration + +#ifndef JSON_LIBRARY + #define DECL_SET_INTEGER(type) void Set(type); void Set(unsigned type); +#endif + +#ifdef JSON_MUTEX_CALLBACKS + #define initializeMutex(x) ,mylock(x) +#else + #define initializeMutex(x) +#endif + +#ifdef JSON_PREPARSE + #define SetFetched(b) (void)0 + #define Fetch() (void)0 + #define initializeFetch(x) +#else + #define initializeFetch(x) ,fetched(x) +#endif + +#ifdef JSON_REF_COUNT + #define initializeRefCount(x) ,refcount(x) +#else + #define initializeRefCount(x) +#endif + +#ifdef JSON_COMMENTS + #define initializeComment(x) ,_comment(x) +#else + #define initializeComment(x) +#endif + +#ifndef JSON_UNIT_TEST + #define incAllocCount() (void)0 + #define decAllocCount() (void)0 + #define incinternalAllocCount() (void)0 + #define decinternalAllocCount() (void)0 +#endif + +#ifdef JSON_VALIDATE + #define initializeValid(x) ,isValid(x) +#else + #define initializeValid(x) +#endif + +class internalJSONNode { +public: + internalJSONNode(char mytype = JSON_NULL); + internalJSONNode(const json_string & unparsed); + internalJSONNode(const json_string & name_t, const json_string & value_t); + internalJSONNode(const internalJSONNode & orig); + internalJSONNode & operator = (const internalJSONNode &); + ~internalJSONNode(void); + + static internalJSONNode * newInternal(char mytype = JSON_NULL); + static internalJSONNode * newInternal(const json_string & unparsed); + static internalJSONNode * newInternal(const json_string & name_t, const json_string & value_t); + static internalJSONNode * newInternal(const internalJSONNode & orig); //not copyable, only by this class + static void deleteInternal(internalJSONNode * ptr); + + json_index_t size(void) const; + bool empty(void) const; + unsigned char type(void) const; + + json_string name(void) const; + void setname(const json_string & newname); + #ifdef JSON_COMMENTS + void setcomment(const json_string & comment); + json_string getcomment(void) const; + #endif + json_string as_string(void) const; + long as_int(void) const; + json_number as_float(void) const; + bool as_bool(void) const; + + #ifndef JSON_PREPARSE + void preparse(void); + #endif + + #ifdef JSON_LIBRARY + void push_back(JSONNode * node); + #else + void push_back(const JSONNode & node); + #endif + void reserve(json_index_t size); + void push_front(const JSONNode & node); + JSONNode * pop_back(json_index_t pos); + JSONNode * pop_back(const json_string & name_t); + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNode * pop_back_nocase(const json_string & name_t); + #endif + + JSONNode * at(json_index_t pos); + //These return ** because pop_back needs them + JSONNode ** at(const json_string & name_t); + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNode ** at_nocase(const json_string & name_t); + #endif + + void Set(const json_string & val); + #ifdef JSON_LIBRARY + void Set(json_number val); + void Set(long val); + #else + DECL_SET_INTEGER(char) + DECL_SET_INTEGER(short) + DECL_SET_INTEGER(int) + DECL_SET_INTEGER(long) + #ifndef JSON_ISO_STRICT + DECL_SET_INTEGER(long long) + #endif + + void Set(float val); + void Set(double val); + #endif + void Set(bool val); + + bool IsEqualTo(const json_string & val)const ; + bool IsEqualTo(bool val) const; + bool IsEqualTo(const internalJSONNode * val) const; + + template<typename T> + bool IsEqualToNum(T val) const; + + internalJSONNode * incRef(void); + #ifdef JSON_REF_COUNT + void decRef(void); + bool hasNoReferences(void); + #endif + internalJSONNode * makeUnique(void); + + JSONNode ** begin(void) const; + JSONNode ** end(void) const; + #ifdef JSON_REF_COUNT + size_t refcount BITS(20); + #endif + bool Fetched(void) const; + #ifdef JSON_MUTEX_CALLBACKS + void * mylock; + void _set_mutex(void * mutex, bool unset = true); + void _unset_mutex(void); + #endif + #ifdef JSON_UNIT_TEST + static void incinternalAllocCount(void); + static void decinternalAllocCount(void); + #endif + + #ifdef JSON_WRITER + json_string WriteName(bool formatted, bool arrayChild) const; + json_string WriteChildren(unsigned int indent); + json_string WriteComment(unsigned int indent) const; + json_string Write(unsigned int indent, bool arrayChild); + #endif + #ifdef JSON_DEBUG + #ifndef JSON_LIBRARY + JSONNode Dump(size_t & totalmemory) const; + JSONNode DumpMutex(void) const; + #endif + #endif + + //json parts + mutable unsigned char _type BITS(3); + mutable bool _name_encoded BITS(1); //must be above name due to initialization list order + json_string _name; + + mutable json_string _string; //these are both mutable because the string can change when it's fetched + mutable bool _string_encoded BITS(1); + + //the value of the json + union value_union_t { + bool _bool; + json_number _number; + }; + mutable value_union_t _value; //internal structure changes depending on type + + jsonChildren Children; //container that holds all of my children + + #ifdef JSON_VALIDATE + mutable bool isValid BITS(1); //this does not need to be initialized, it's only used if it's null + void Nullify(bool validation = true) const; + bool validate(void); + #else + void Nullify(void) const; + #endif + + //Fetching and such + #ifndef JSON_PREPARSE + mutable bool fetched BITS(1); + void SetFetched(bool val) const; + void Fetch(void) const; //it's const because it doesn't change the VALUE of the function + #endif + + #ifdef JSON_COMMENTS + json_string _comment; + #endif + + void FetchString(void) const; + void FetchNode(void) const; + void FetchArray(void) const; + void FetchNumber(void) const; + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + static bool AreEqualNoCase(const json_char * ch_one, const json_char * ch_two); + #endif +}; + +inline internalJSONNode::internalJSONNode(char mytype) : _type(mytype), Children(), _name(), _name_encoded(), _string(), _string_encoded(), _value() + initializeMutex(0) + initializeRefCount(1) + initializeFetch(true) + initializeComment(0) + initializeValid(true){ + + incinternalAllocCount(); +} + +inline internalJSONNode * internalJSONNode::incRef(void){ + #ifdef JSON_REF_COUNT + ++refcount; + return this; + #else + return makeUnique(); + #endif +} + +inline json_index_t internalJSONNode::size(void) const { + Fetch(); + return Children.size(); +} + +inline bool internalJSONNode::empty(void) const { + if (type() != JSON_NODE && type() != JSON_ARRAY) return true; + Fetch(); + return Children.empty(); +} + +inline unsigned char internalJSONNode::type(void) const { + #ifdef JSON_LESS_MEMORY + return _type & 0xF; + #else + return _type; + #endif +} + +inline json_string internalJSONNode::name(void) const { + return _name; +} + +inline void internalJSONNode::setname(const json_string & newname){ + _name = newname; + #ifdef JSON_LESS_MEMORY + _type |= 0x10; + #else + _name_encoded = true; + #endif +} + +#ifdef JSON_COMMENTS + inline void internalJSONNode::setcomment(const json_string & comment){ + _comment = comment; + } + + inline json_string internalJSONNode::getcomment(void) const { + return _comment; + } +#endif + +inline json_string internalJSONNode::as_string(void) const { + Fetch(); + return _string; +} + +inline long internalJSONNode::as_int(void) const { + Fetch(); + switch(type()) { + case JSON_NULL: + return 0; + case JSON_BOOL: + return _value._bool ? 1 : 0; + } + JSON_ASSERT(type() == JSON_NUMBER, JSON_TEXT("as_int returning undefined results")); + JSON_ASSERT(_value._number > LONG_MIN, _string + JSON_TEXT(" is outside the lower range of long")); + JSON_ASSERT(_value._number < LONG_MAX, _string + JSON_TEXT(" is outside the upper range of long")); + JSON_ASSERT(_value._number == (json_number)((int)_value._number), json_string(JSON_TEXT("as_int will truncate ")) + _string); + return (int)_value._number; +} + +inline json_number internalJSONNode::as_float(void) const { + Fetch(); + switch(type()) { + case JSON_NULL: + return (json_number)0.0; + case JSON_BOOL: + return (json_number)(_value._bool ? 1.0 : 0.0); + } + JSON_ASSERT(type() == JSON_NUMBER, JSON_TEXT("as_float returning undefined results")); + return _value._number; +} + +inline bool internalJSONNode::as_bool(void) const { + Fetch(); + switch(type()) { + case JSON_NUMBER: + return _value._number != 0.0f; + case JSON_NULL: + return false; + } + JSON_ASSERT(type() == JSON_BOOL, JSON_TEXT("as_bool returning undefined results")); + return _value._bool; +} + +inline bool internalJSONNode::IsEqualTo(const json_string & val) const { + if (type() != JSON_STRING) return false; + Fetch(); + return val == _string; +} + +inline bool internalJSONNode::IsEqualTo(bool val) const { + if (type() != JSON_BOOL) return false; + Fetch(); + return val == _value._bool; +} + +template<typename T> +inline bool internalJSONNode::IsEqualToNum(T val) const { + if (type() != JSON_NUMBER) return false; + Fetch(); + return (json_number)val == _value._number; +} + +#ifdef JSON_REF_COUNT + inline void internalJSONNode::decRef(void){ + JSON_ASSERT(refcount != 0, JSON_TEXT("decRef on a 0 refcount internal")); + --refcount; + } + + inline bool internalJSONNode::hasNoReferences(void){ + return refcount == 0; + } +#endif + +inline internalJSONNode * internalJSONNode::makeUnique(void){ + #ifdef JSON_REF_COUNT + if (refcount > 1){ + decRef(); + return newInternal(*this); + } + JSON_ASSERT(refcount == 1, JSON_TEXT("makeUnique on a 0 refcount internal")); + return this; + #else + return newInternal(*this); + #endif +} + +#ifndef JSON_PREPARSE + inline void internalJSONNode::SetFetched(bool val) const { + fetched = val; + } +#endif + +inline bool internalJSONNode::Fetched(void) const { + #ifndef JSON_PREPARSE + return fetched; + #else + return true; + #endif +} + +inline JSONNode ** internalJSONNode::begin(void) const { + Fetch(); + return Children.begin(); +} + +inline JSONNode ** internalJSONNode::end(void) const { + Fetch(); + return Children.end(); +} + +inline JSONNode * internalJSONNode::at(json_index_t pos){ + Fetch(); + return Children[pos]; +} + +inline void internalJSONNode::reserve(json_index_t size){ + Fetch(); + Children.reserve2(size); +} + +/* + These functions are to allow allocation to be completely controlled by the callbacks +*/ + +inline void internalJSONNode::deleteInternal(internalJSONNode * ptr){ + #ifdef JSON_MEMORY_CALLBACKS + ptr -> ~internalJSONNode(); + libjson_free<internalJSONNode>(ptr); + #else + delete ptr; + #endif +} + +inline internalJSONNode * internalJSONNode::newInternal(char mytype){ + #ifdef JSON_MEMORY_CALLBACKS + return new(json_malloc<internalJSONNode>(1)) internalJSONNode(mytype); + #else + return new internalJSONNode(mytype); + #endif +} + +inline internalJSONNode * internalJSONNode::newInternal(const json_string & unparsed){ + #ifdef JSON_MEMORY_CALLBACKS + return new(json_malloc<internalJSONNode>(1)) internalJSONNode(unparsed); + #else + return new internalJSONNode(unparsed); + #endif +} + +inline internalJSONNode * internalJSONNode::newInternal(const json_string & name_t, const json_string & value_t){ + #ifdef JSON_MEMORY_CALLBACKS + return new(json_malloc<internalJSONNode>(1)) internalJSONNode(name_t, value_t); + #else + return new internalJSONNode(name_t, value_t); + #endif +} + +inline internalJSONNode * internalJSONNode::newInternal(const internalJSONNode & orig){ + #ifdef JSON_MEMORY_CALLBACKS + return new(json_malloc<internalJSONNode>(1)) internalJSONNode(orig); + #else + return new internalJSONNode(orig); + #endif +} + + +#endif diff --git a/src/modules/json/json.cpp b/src/modules/json/json.cpp new file mode 100644 index 0000000000..ee71ee4ef7 --- /dev/null +++ b/src/modules/json/json.cpp @@ -0,0 +1,134 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+#include <m_json.h>
+
+#include "libJSON.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Interface function
+
+INT_PTR GetInterface(WPARAM wParam, LPARAM lParam)
+{
+ LPJSONSERVICEINTERFACE lpJSI=(LPJSONSERVICEINTERFACE)wParam;
+ memset(lpJSI,0,sizeof(JSONSERVICEINTERFACE));
+
+#define SETJSI(x) lpJSI->##x=json_##x
+ SETJSI(free);
+ lpJSI->delete_=json_delete;
+#ifdef JSON_MEMORY_MANAGE
+ SETJSI(free_all);
+ SETJSI(delete_all);
+#endif
+ SETJSI(parse);
+ SETJSI(strip_white_space);
+#ifdef JSON_VALIDATE
+ SETJSI(validate);
+#endif
+ SETJSI(new_a);
+ SETJSI(new_i);
+ SETJSI(new_f);
+ SETJSI(new_b);
+ lpJSI->new_=json_new;
+ SETJSI(copy);
+ SETJSI(duplicate);
+ SETJSI(set_a);
+ SETJSI(set_i);
+ SETJSI(set_f);
+ SETJSI(set_b);
+ SETJSI(set_n);
+ SETJSI(type);
+ SETJSI(size);
+ SETJSI(empty);
+ SETJSI(name);
+#ifdef JSON_COMMENTS
+ SETJSI(get_comment);
+#endif
+ SETJSI(as_string);
+ SETJSI(as_int);
+ SETJSI(as_float);
+ SETJSI(as_bool);
+ SETJSI(as_node);
+ SETJSI(as_array);
+ #ifdef JSON_BINARY
+ SETJSI(as_binary);
+ #endif
+ #ifdef JSON_WRITER
+ SETJSI(write);
+ SETJSI(write_formatted);
+ #endif
+ SETJSI(set_name);
+ #ifdef JSON_COMMENTS
+ SETJSI(set_comment);
+ #endif
+ SETJSI(clear);
+ SETJSI(nullify);
+ SETJSI(swap);
+ SETJSI(merge);
+ #ifndef JSON_PREPARSE
+ SETJSI(preparse);
+ #endif
+ #ifdef JSON_BINARY
+ SETJSI(set_binary);
+ #endif
+ SETJSI(cast);
+
+ //children access
+ SETJSI(reserve);
+ SETJSI(at);
+ SETJSI(get);
+ #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS
+ SETJSI(get_nocase);
+ SETJSI(pop_back_nocase);
+ #endif
+ SETJSI(push_back);
+ SETJSI(pop_back_at);
+ SETJSI(pop_back);
+ #ifdef JSON_ITERATORS
+ SETJSI(find);
+ #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS
+ SETJSI(find_nocase);
+ #endif
+ SETJSI(erase);
+ SETJSI(erase_multi);
+ SETJSI(insert);
+ SETJSI(insert_multi);
+
+ //iterator functions
+ SETJSI(begin);
+ SETJSI(end);
+ #endif
+
+ SETJSI(equal);
+
+ return 0;
+}
+
+int InitJson()
+{
+ CreateServiceFunction(MS_JSON_GETINTERFACE,GetInterface);
+
+ json_register_memory_callbacks((json_malloc_t)mir_alloc,(json_realloc_t)mir_realloc,(json_free_t)mir_free);
+ return 0;
+}
diff --git a/src/modules/json/libJSON.cpp b/src/modules/json/libJSON.cpp new file mode 100644 index 0000000000..a27461f275 --- /dev/null +++ b/src/modules/json/libJSON.cpp @@ -0,0 +1,507 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "..\..\core\commonheaders.h"
+
+#include "libJSON.h"
+ +/* + This is the implementation of the C interface to libJSON + This file may be included in any C++ application, but it will + be completely ignored if JSON_LIBRARY isn't defined. The + only reason JSON_LIBRARY should be defined is when compiling libJSON + as a library +*/ + +#ifdef JSON_LIBRARY + + #include "JSONNode.h" + #include "JSONWorker.h" + #include <stdexcept> //some methods throw exceptions + #ifdef JSON_MEMORY_MANAGE + auto_expand StringHandler; + auto_expand_node NodeHandler; + #define MANAGER_INSERT(x) NodeHandler.insert(x) + #else + #define MANAGER_INSERT(x) x + #endif + + #ifdef JSON_SAFE + static const json_char * EMPTY_CSTRING = JSON_TEXT(""); + #endif + + inline json_char * toCString(const json_string & str){ + const size_t len((str.length() + 1) * sizeof(json_char)); + #ifdef JSON_MEMORY_MANAGE + return (json_char *)StringHandler.insert(memcpy(json_malloc<json_char>(len), str.c_str(), len)); + #else + return (json_char *)memcpy(json_malloc<json_char>(len), str.c_str(), len); + #endif + } + + /* + stuff that's in namespace libJSON + */ + void json_free(void * str){ + JSON_ASSERT_SAFE(str, JSON_TEXT("freeing null ptr"), return;); + #ifdef JSON_MEMORY_MANAGE + StringHandler.remove(str); + #endif + libjson_free<void>(str); + } + + void json_delete(JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("deleting null ptr"), return;); + #ifdef JSON_MEMORY_MANAGE + NodeHandler.remove(node); + #endif + JSONNode::deleteJSONNode((JSONNode *)node); + } + + #ifdef JSON_MEMORY_MANAGE + void json_free_all(void){ + StringHandler.clear(); + } + + void json_delete_all(void){ + NodeHandler.clear(); + } + #endif + + JSONNODE * json_parse(const json_char * json){ + JSON_ASSERT_SAFE(json, JSON_TEXT("null ptr to json_parse"), return 0;); + try { + //use this constructor to simply copy reference instead of copying the temp + return MANAGER_INSERT(JSONNode::newJSONNode_Shallow(JSONWorker::parse(json))); + } catch (std::invalid_argument){} + return 0; + } + + json_char * json_strip_white_space(const json_char * json){ + JSON_ASSERT_SAFE(json, JSON_TEXT("null ptr to json_strip_white_space"), return 0;); + return toCString(JSONWorker::RemoveWhiteSpaceAndComments(json)); + } + + #ifdef JSON_VALIDATE + JSONNODE * json_validate(const json_char * json){ + JSON_ASSERT_SAFE(json, JSON_TEXT("null ptr to json_validate"), return 0;); + try { + //use this constructor to simply copy reference instead of copying the temp + return MANAGER_INSERT(JSONNode::newJSONNode_Shallow(JSONWorker::validate(json))); + } catch (std::invalid_argument){} + return 0; + } + #endif + + #if defined JSON_DEBUG && !defined JSON_STDERROR + //When libjson errors, a callback allows the user to know what went wrong + void json_register_debug_callback(json_error_callback_t callback){ + JSONDebug::register_callback(callback); + } + #endif + + #ifdef JSON_MUTEX_CALLBACKS + #ifdef JSON_MUTEX_MANAGE + void json_register_mutex_callbacks(json_mutex_callback_t lock, json_mutex_callback_t unlock, json_mutex_callback_t destroy, void * manager_lock){ + JSONNode::register_mutex_callbacks(lock, unlock, manager_lock); + JSONNode::register_mutex_destructor(destroy); + } + + #else + void json_register_mutex_callbacks(json_mutex_callback_t lock, json_mutex_callback_t unlock, void * manager_lock){ + JSONNode::register_mutex_callbacks(lock, unlock, manager_lock); + } + #endif + + void json_set_global_mutex(void * mutex){ + JSONNode::set_global_mutex(mutex); + } + + void json_set_mutex(JSONNODE * node, void * mutex){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_set_mutex"), return;); + ((JSONNode*)node) -> set_mutex(mutex); + } + + void json_lock(JSONNODE * node, int threadid){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_lock"), return;); + ((JSONNode*)node) -> lock(threadid); + } + + void json_unlock(JSONNODE * node, int threadid){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_unlock"), return;); + ((JSONNode*)node) -> unlock(threadid); + } + #endif + + #ifdef JSON_MEMORY_CALLBACKS + void json_register_memory_callbacks(json_malloc_t mal, json_realloc_t real, json_free_t fre){ + JSONMemory::registerMemoryCallbacks(mal, real, fre); + } + #endif + + + /* + stuff that's in class JSONNode + */ + //ctors + JSONNODE * json_new_a(const json_char * name, const json_char * value){ + JSON_ASSERT_SAFE(name, JSON_TEXT("null name to json_new_a"), name = EMPTY_CSTRING;); + JSON_ASSERT_SAFE(value, JSON_TEXT("null value to json_new_a"), value = EMPTY_CSTRING;); + #ifdef JSON_MEMORY_CALLBACKS + return MANAGER_INSERT(new(json_malloc<JSONNode>(1)) JSONNode(name, json_string(value))); + #else + return MANAGER_INSERT(new JSONNode(name, json_string(value))); + #endif + } + + JSONNODE * json_new_i(const json_char * name, long value){ + JSON_ASSERT_SAFE(name, JSON_TEXT("null name to json_new_i"), name = EMPTY_CSTRING;); + #ifdef JSON_MEMORY_CALLBACKS + return MANAGER_INSERT(new(json_malloc<JSONNode>(1)) JSONNode(name, value)); + #else + return MANAGER_INSERT(new JSONNode(name, value)); + #endif + } + + JSONNODE * json_new_f(const json_char * name, json_number value){ + JSON_ASSERT_SAFE(name, JSON_TEXT("null name to json_new_f"), name = EMPTY_CSTRING;); + #ifdef JSON_MEMORY_CALLBACKS + return MANAGER_INSERT(new(json_malloc<JSONNode>(1)) JSONNode(name, value)); + #else + return MANAGER_INSERT(new JSONNode(name, value)); + #endif + } + + JSONNODE * json_new_b(const json_char * name, int value){ + JSON_ASSERT_SAFE(name, JSON_TEXT("null name to json_new_b"), name = EMPTY_CSTRING;); + #ifdef JSON_MEMORY_CALLBACKS + return MANAGER_INSERT(new(json_malloc<JSONNode>(1)) JSONNode(name, (bool)value)); + #else + return MANAGER_INSERT(new JSONNode(name, (bool)value)); + #endif + } + + JSONNODE * json_new(char type){ + #ifdef JSON_MEMORY_CALLBACKS + return MANAGER_INSERT(new(json_malloc<JSONNode>(1)) JSONNode(type)); + #else + return MANAGER_INSERT(new JSONNode(type)); + #endif + } + + JSONNODE * json_copy(const JSONNODE * orig){ + JSON_ASSERT_SAFE(orig, JSON_TEXT("null orig to json_copy"), return 0;); + #ifdef JSON_MEMORY_CALLBACKS + return MANAGER_INSERT(new(json_malloc<JSONNode>(1)) JSONNode(*((JSONNode*)orig))); + #else + return MANAGER_INSERT(new JSONNode(*((JSONNode*)orig))); + #endif + } + + JSONNODE * json_duplicate(const JSONNODE * orig){ + JSON_ASSERT_SAFE(orig, JSON_TEXT("null orig to json_duplicate"), return 0;); + return MANAGER_INSERT(JSONNode::newJSONNode_Shallow(((JSONNode*)orig) -> duplicate())); + } + + //assignment + void json_set_a(JSONNODE * node, const json_char * value){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_set_a"), return;); + JSON_ASSERT_SAFE(value, JSON_TEXT("null value to json_set_a"), value = EMPTY_CSTRING;); + *((JSONNode*)node) = json_string(value); + } + + void json_set_i(JSONNODE * node, long value){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_set_i"), return;); + *((JSONNode*)node) = value; + } + + void json_set_f(JSONNODE * node, json_number value){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_set_f"), return;); + *((JSONNode*)node) = value; + } + + void json_set_b(JSONNODE * node, int value){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_set_b"), return;); + *((JSONNode*)node) = (bool)value; + } + + void json_set_n(JSONNODE * node, const JSONNODE * orig){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_set_n"), return;); + JSON_ASSERT_SAFE(orig, JSON_TEXT("null node to json_set_n"), return;); + *((JSONNode*)node) = *((JSONNode*)orig); + } + + + //inspectors + char json_type(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_type"), return JSON_NULL;); + return ((JSONNode*)node) -> type(); + } + + json_index_t json_size(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_size"), return 0;); + return ((JSONNode*)node) -> size(); + } + + int json_empty(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_empty"), return true;); + return (int)(((JSONNode*)node) -> empty()); + } + + json_char * json_name(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_name"), return toCString(EMPTY_CSTRING);); + return toCString(((JSONNode*)node) -> name()); + } + + #ifdef JSON_COMMENTS + json_char * json_get_comment(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_get_comment"), return toCString(EMPTY_CSTRING);); + return toCString(((JSONNode*)node) -> get_comment()); + } + #endif + + json_char * json_as_string(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_as_string"), return toCString(EMPTY_CSTRING);); + return toCString(((JSONNode*)node) -> as_string()); + } + + long json_as_int(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_as_int"), return 0;); + return ((JSONNode*)node) -> as_int(); + } + + json_number json_as_float(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_as_float"), return 0.0;); + return ((JSONNode*)node) -> as_float(); + } + + int json_as_bool(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_as_bool"), return false;); + return (int)(((JSONNode*)node) -> as_bool()); + } + + JSONNODE * json_as_node(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_as_node"), return 0;); + return MANAGER_INSERT(JSONNode::newJSONNode_Shallow(((JSONNode*)node) -> as_node())); + } + + JSONNODE * json_as_array(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_as_array"), return 0;); + return MANAGER_INSERT(JSONNode::newJSONNode_Shallow(((JSONNode*)node) -> as_array())); + } + + #ifdef JSON_BINARY + void * json_as_binary(const JSONNODE * node, unsigned long * size){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_as_binary"), if (size){*size = 0;} return 0;); + const std::string result = ((JSONNode*)node) -> as_binary(); + const size_t len = result.length(); + if (size) *size = len; + #ifdef JSON_SAFE + if (result.empty()) return 0; + #endif + #ifdef JSON_MEMORY_MANAGE + return StringHandler.insert(memcpy(json_malloc<char>(len), result.data(), len)); + #else + return memcpy(json_malloc<char>(len), result.data(), len); + #endif + } + #endif + + #ifdef JSON_WRITER + json_char * json_write(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_write"), return toCString(EMPTY_CSTRING);); + return toCString(((JSONNode*)node) -> write()); + } + + json_char * json_write_formatted(const JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_write_formatted"), return toCString(EMPTY_CSTRING);); + return toCString(((JSONNode*)node) -> write_formatted()); + } + #endif + + //modifiers + void json_set_name(JSONNODE * node, const json_char * name){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_set_name"), return;); + JSON_ASSERT_SAFE(name, JSON_TEXT("null name to json_set_name"), name = EMPTY_CSTRING;); + ((JSONNode*)node) -> set_name(name); + } + + #ifdef JSON_COMMENTS + void json_set_comment(JSONNODE * node, const json_char * comment){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_set_comment"), return;); + JSON_ASSERT_SAFE(comment, JSON_TEXT("null name to json_set_comment"), comment = EMPTY_CSTRING;); + ((JSONNode*)node) -> set_comment(comment); + } + #endif + + void json_clear(JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_clear"), return;); + ((JSONNode*)node) -> clear(); + } + + void json_nullify(JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_nullify"), return;); + ((JSONNode*)node) -> nullify(); + } + + void json_swap(JSONNODE * node, JSONNODE * node2){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_swap"), return;); + JSON_ASSERT_SAFE(node2, JSON_TEXT("null node to json_swap"), return;); + ((JSONNode*)node) -> swap(*(JSONNode*)node2); + } + + void json_merge(JSONNODE * node, JSONNODE * node2){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_merge"), return;); + JSON_ASSERT_SAFE(node2, JSON_TEXT("null node to json_merge"), return;); + ((JSONNode*)node) -> merge(*(JSONNode*)node2); + } + + #ifndef JSON_PREPARSE + void json_preparse(JSONNODE * node){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_preparse"), return;); + ((JSONNode*)node) -> preparse(); + } + #endif + + #ifdef JSON_BINARY + void json_set_binary(JSONNODE * node, const void * data, unsigned long length){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_swap"), return;); + JSON_ASSERT_SAFE(data, JSON_TEXT("null data to json_set_binary"), *((JSONNode*)node) = EMPTY_CSTRING; return;); + ((JSONNode*)node) -> set_binary((unsigned char *)data, length); + } + #endif + + void json_cast(JSONNODE * node, char type){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_cast"), return;); + ((JSONNode*)node) -> cast(type); + } + + //children access + void json_reserve(JSONNODE * node, json_index_t siz){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_reserve"), return;); + ((JSONNode*)node) -> reserve(siz); + } + + JSONNODE * json_at(JSONNODE * node, unsigned int pos){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_at"), return 0;); + try { + return &((JSONNode*)node) -> at(pos); + } catch (std::out_of_range){} + return 0; + } + + JSONNODE * json_get(JSONNODE * node, const json_char * name){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_get"), return 0;); + JSON_ASSERT_SAFE(name, JSON_TEXT("null node to json_get. Did you mean to use json_at?"), return 0;); + try { + return &((JSONNode*)node) -> at(name); + } catch (std::out_of_range){} + return 0; + } + + + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNODE * json_get_nocase(JSONNODE * node, const json_char * name){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_at_nocase"), return 0;); + JSON_ASSERT_SAFE(name, JSON_TEXT("null name to json_at_nocase"), return 0;); + try { + return &((JSONNode*)node) -> at_nocase(name); + } catch (std::out_of_range){} + return 0; + } + + JSONNODE * json_pop_back_nocase(JSONNODE * node, const json_char * name){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_pop_back_nocase"), return 0;); + JSON_ASSERT_SAFE(name, JSON_TEXT("null name to json_pop_back_nocase"), return 0;); + return MANAGER_INSERT(((JSONNode*)node) -> pop_back_nocase(name)); + } + #endif + + void json_push_back(JSONNODE * node, JSONNODE * node2){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_push_back"), return;); + JSON_ASSERT_SAFE(node2, JSON_TEXT("null node2 to json_push_back"), return;); + #ifdef JSON_MEMORY_MANAGE + NodeHandler.remove(node2); + #endif + ((JSONNode*)node) -> push_back((JSONNode*)node2); + } + + JSONNODE * json_pop_back_at(JSONNODE * node, unsigned int pos){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_pop_back_i"), return 0;); + return MANAGER_INSERT(((JSONNode*)node) -> pop_back(pos)); + } + + JSONNODE * json_pop_back(JSONNODE * node, const json_char * name){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_pop_back"), return 0;); + JSON_ASSERT_SAFE(name, JSON_TEXT("null name to json_pop_back. Did you mean to use json_pop_back_at?"), return 0;); + return MANAGER_INSERT(((JSONNode*)node) -> pop_back(name)); + } + + #ifdef JSON_ITERATORS + JSONNODE_ITERATOR json_find(JSONNODE * node, const json_char * name){ + return (JSONNODE_ITERATOR)(((JSONNode*)node) -> find(name)); + } + + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + JSONNODE_ITERATOR json_find_nocase(JSONNODE * node, const json_char * name){ + return (JSONNODE_ITERATOR)(((JSONNode*)node) -> find_nocase(name)); + } + #endif + + JSONNODE_ITERATOR json_erase(JSONNODE * node, JSONNODE_ITERATOR it){ + return (JSONNODE_ITERATOR)(((JSONNode*)node) -> erase((JSONNode**)it)); + } + + JSONNODE_ITERATOR json_erase_multi(JSONNODE * node, JSONNODE_ITERATOR start, JSONNODE_ITERATOR end){ + return (JSONNODE_ITERATOR)(((JSONNode*)node) -> erase((JSONNode**)start, (JSONNode**)end)); + } + + JSONNODE_ITERATOR json_insert(JSONNODE * node, JSONNODE_ITERATOR it, JSONNODE * node2){ + #ifdef JSON_MEMORY_MANAGE + NodeHandler.remove(node2); + #endif + return (JSONNODE_ITERATOR)(((JSONNode*)node) -> insert((JSONNode**)it, (JSONNode*)node2)); + } + + JSONNODE_ITERATOR json_insert_multi(JSONNODE * node, JSONNODE_ITERATOR it, JSONNODE_ITERATOR start, JSONNODE_ITERATOR end){ + return (JSONNODE_ITERATOR)(((JSONNode*)node) -> insert((JSONNode**)it, (JSONNode**)start, (JSONNode**)end)); + } + + //iterator functions + JSONNODE_ITERATOR json_begin(JSONNODE * node){ + return (JSONNODE_ITERATOR)(((JSONNode*)node) -> begin()); + } + + JSONNODE_ITERATOR json_end(JSONNODE * node){ + return (JSONNODE_ITERATOR)(((JSONNode*)node) -> end()); + } + #endif + + //comparison + int json_equal(JSONNODE * node, JSONNODE * node2){ + JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_equal"), return false;); + JSON_ASSERT_SAFE(node2, JSON_TEXT("null node2 to json_equal"), return false;); + return (int)(*((JSONNode*)node) == *((JSONNode*)node2)); + } + +#endif //JSON_LIBRARY diff --git a/src/modules/json/libJSON.h b/src/modules/json/libJSON.h new file mode 100644 index 0000000000..3e60fd502a --- /dev/null +++ b/src/modules/json/libJSON.h @@ -0,0 +1,212 @@ +#ifndef LIBJSON_H +#define LIBJSON_H + +#include "JSONDefs.h" //for typedefs of functions, strings, and nodes + +#ifdef _STATIC +#define LIBJSON_DLL +#elif LIBJSON_EXPORTS +#define LIBJSON_DLL _declspec(dllexport) +#else +#define LIBJSON_DLL _declspec(dllimport) +#endif + +/* + This is the C interface to libJSON. + + This file also declares various things that are needed for + C++ programming +*/ + +#ifdef JSON_LIBRARY //compiling the library, hide the interface + #ifdef __cplusplus + #ifdef JSON_UNIT_TEST + #include "JSONNode.h" + #endif + extern "C" { + #endif + + /* + stuff that's in namespace libJSON + */ + LIBJSON_DLL void json_free(void * str); + LIBJSON_DLL void json_delete(JSONNODE * node); + #ifdef JSON_MEMORY_MANAGE + LIBJSON_DLL void json_free_all(void); + LIBJSON_DLL void json_delete_all(void); + #endif + LIBJSON_DLL JSONNODE * json_parse(const json_char * json); + LIBJSON_DLL json_char * json_strip_white_space(const json_char * json); + #ifdef JSON_VALIDATE + LIBJSON_DLL JSONNODE * json_validate(const json_char * json); + #endif + #if defined JSON_DEBUG && !defined JSON_STDERROR + //When libjson errors, a callback allows the user to know what went wrong + LIBJSON_DLL void json_register_debug_callback(json_error_callback_t callback); + #endif + #ifdef JSON_MUTEX_CALLBACKS + #ifdef JSON_MUTEX_MANAGE + LIBJSON_DLL void json_register_mutex_callbacks(json_mutex_callback_t lock, json_mutex_callback_t unlock, json_mutex_callback_t destroy, void * manager_lock); + #else + LIBJSON_DLL void json_register_mutex_callbacks(json_mutex_callback_t lock, json_mutex_callback_t unlock, void * manager_lock); + #endif + LIBJSON_DLL void json_set_global_mutex(void * mutex); + LIBJSON_DLL void json_set_mutex(JSONNODE * node, void * mutex); + LIBJSON_DLL void json_lock(JSONNODE * node, int threadid); + LIBJSON_DLL void json_unlock(JSONNODE * node, int threadid); + #endif + #ifdef JSON_MEMORY_CALLBACKS + LIBJSON_DLL void json_register_memory_callbacks(json_malloc_t mal, json_realloc_t real, json_free_t fre); + #endif + + + /* + stuff that's in class JSONNode + */ + //ctors + LIBJSON_DLL JSONNODE * json_new_a(const json_char * name, const json_char * value); + LIBJSON_DLL JSONNODE * json_new_i(const json_char * name, long value); + LIBJSON_DLL JSONNODE * json_new_f(const json_char * name, json_number value); + LIBJSON_DLL JSONNODE * json_new_b(const json_char * name, int value); //because C bools are ints and C++ will implicitly cast it + LIBJSON_DLL JSONNODE * json_new(char type); + LIBJSON_DLL JSONNODE * json_copy(const JSONNODE * orig); + LIBJSON_DLL JSONNODE * json_duplicate(const JSONNODE * orig); + + //assignment + LIBJSON_DLL void json_set_a(JSONNODE * node, const json_char * value); + LIBJSON_DLL void json_set_i(JSONNODE * node, long value); + LIBJSON_DLL void json_set_f(JSONNODE * node, json_number value); + LIBJSON_DLL void json_set_b(JSONNODE * node, int value); //because C bools are ints ane C++ will implicit + LIBJSON_DLL void json_set_n(JSONNODE * node, const JSONNODE * orig); + + //inspectors + LIBJSON_DLL char json_type(const JSONNODE * node); + LIBJSON_DLL json_index_t json_size(const JSONNODE * node); + LIBJSON_DLL int json_empty(const JSONNODE * node); + LIBJSON_DLL json_char * json_name(const JSONNODE * node); + #ifdef JSON_COMMENTS + LIBJSON_DLL json_char * json_get_comment(const JSONNODE * node); + #endif + LIBJSON_DLL json_char * json_as_string(const JSONNODE * node); + LIBJSON_DLL long json_as_int(const JSONNODE * node); + LIBJSON_DLL json_number json_as_float(const JSONNODE * node); + LIBJSON_DLL int json_as_bool(const JSONNODE * node); + LIBJSON_DLL JSONNODE * json_as_node(const JSONNODE * node); + LIBJSON_DLL JSONNODE * json_as_array(const JSONNODE * node); + #ifdef JSON_BINARY + LIBJSON_DLL void * json_as_binary(const JSONNODE * node, unsigned long * size); + #endif + #ifdef JSON_WRITER + LIBJSON_DLL json_char * json_write(const JSONNODE * node); + LIBJSON_DLL json_char * json_write_formatted(const JSONNODE * node); + #endif + + //modifiers + LIBJSON_DLL void json_set_name(JSONNODE * node, const json_char * name); + #ifdef JSON_COMMENTS + LIBJSON_DLL void json_set_comment(JSONNODE * node, const json_char * comment); + #endif + LIBJSON_DLL void json_clear(JSONNODE * node); + LIBJSON_DLL void json_nullify(JSONNODE * node); + LIBJSON_DLL void json_swap(JSONNODE * node, JSONNODE * node2); + LIBJSON_DLL void json_merge(JSONNODE * node, JSONNODE * node2); + #ifndef JSON_PREPARSE + LIBJSON_DLL void json_preparse(JSONNODE * node); + #endif + #ifdef JSON_BINARY + LIBJSON_DLL void json_set_binary(JSONNODE * node, const void * data, unsigned long length); + #endif + LIBJSON_DLL void json_cast(JSONNODE * node, char type); + + //children access + LIBJSON_DLL void json_reserve(JSONNODE * node, json_index_t siz); + LIBJSON_DLL JSONNODE * json_at(JSONNODE * node, json_index_t pos); + LIBJSON_DLL JSONNODE * json_get(JSONNODE * node, const json_char * name); + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + LIBJSON_DLL JSONNODE * json_get_nocase(JSONNODE * node, const json_char * name); + LIBJSON_DLL JSONNODE * json_pop_back_nocase(JSONNODE * node, const json_char * name); + #endif + LIBJSON_DLL void json_push_back(JSONNODE * node, JSONNODE * node2); + LIBJSON_DLL JSONNODE * json_pop_back_at(JSONNODE * node, json_index_t pos); + LIBJSON_DLL JSONNODE * json_pop_back(JSONNODE * node, const json_char * name); + #ifdef JSON_ITERATORS + LIBJSON_DLL JSONNODE_ITERATOR json_find(JSONNODE * node, const json_char * name); + #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS + LIBJSON_DLL JSONNODE_ITERATOR json_find_nocase(JSONNODE * node, const json_char * name); + #endif + LIBJSON_DLL JSONNODE_ITERATOR json_erase(JSONNODE * node, JSONNODE_ITERATOR it); + LIBJSON_DLL JSONNODE_ITERATOR json_erase_multi(JSONNODE * node, JSONNODE_ITERATOR start, JSONNODE_ITERATOR end); + LIBJSON_DLL JSONNODE_ITERATOR json_insert(JSONNODE * node, JSONNODE_ITERATOR it, JSONNODE * node2); + LIBJSON_DLL JSONNODE_ITERATOR json_insert_multi(JSONNODE * node, JSONNODE_ITERATOR it, JSONNODE_ITERATOR start, JSONNODE_ITERATOR end); + + //iterator functions + LIBJSON_DLL JSONNODE_ITERATOR json_begin(JSONNODE * node); + LIBJSON_DLL JSONNODE_ITERATOR json_end(JSONNODE * node); + #endif + + //comparison + LIBJSON_DLL int json_equal(JSONNODE * node, JSONNODE * node2); + + #ifdef __cplusplus + } + #endif +#else + #ifndef __cplusplus + #error Using the non-library requires C++ + #endif + #include "JSONNode.h" //not used in this file, but libJSON.h should be the only file required to use it embedded + #include "JSONWorker.h" + #include <stdexcept> //some methods throw exceptions + + namespace libJSON { + //if json is invalid, it throws a std::invalid_argument exception + inline static JSONNode parse(const json_string & json){ + return JSONWorker::parse(json); + } + + //useful if you have json that you don't want to parse, just want to strip to cut down on space + inline static json_string strip_white_space(const json_string & json){ + return JSONWorker::RemoveWhiteSpaceAndComments(json); + } + + //if json is invalid, it throws a std::invalid_argument exception (differs from parse because this checks the entire tree) + #ifdef JSON_VALIDATE + inline static JSONNode validate(const json_string & json){ + return JSONWorker::validate(json); + } + #endif + + //When libjson errors, a callback allows the user to know what went wrong + #if defined JSON_DEBUG && !defined JSON_STDERROR + inline static void register_debug_callback(json_error_callback_t callback){ + JSONDebug::register_callback(callback); + } + #endif + + #ifdef JSON_MUTEX_CALLBACKS + #ifdef JSON_MUTEX_MANAGE + inline static void register_mutex_callbacks(json_mutex_callback_t lock, json_mutex_callback_t unlock, json_mutex_callback_t destroy, void * manager_lock){ + JSONNode::register_mutex_callbacks(lock, unlock, manager_lock); + JSONNode::register_mutex_destructor(destroy); + } + #else + inline static void register_mutex_callbacks(json_mutex_callback_t lock, json_mutex_callback_t unlock, void * manager_lock){ + JSONNode::register_mutex_callbacks(lock, unlock, manager_lock); + } + #endif + + inline static void set_global_mutex(void * mutex){ + JSONNode::set_global_mutex(mutex); + } + #endif + + #ifdef JSON_MEMORY_CALLBACKS + inline static void register_memory_callbacks(json_malloc_t mal, json_realloc_t real, json_free_t fre){ + JSONMemory::registerMemoryCallbacks(mal, real, fre); + } + #endif + + } +#endif //JSON_LIBRARY + +#endif //LIBJSON_H diff --git a/src/modules/utils/utils.cpp b/src/modules/utils/utils.cpp index 96b69de775..c9c1e3a65e 100644 --- a/src/modules/utils/utils.cpp +++ b/src/modules/utils/utils.cpp @@ -28,6 +28,7 @@ int InitWindowList(void); void FreeWindowList(void);
int InitHyperlink(void);
int InitColourPicker(void);
+int InitJson(void);
int InitBitmapFilter(void);
void InitXmlApi(void);
void InitTimeZones(void);
@@ -572,6 +573,7 @@ int LoadUtilsModule(void) InitColourPicker();
InitBitmapFilter();
InitXmlApi();
+ InitJson();
InitTimeZones();
return 0;
}
|