From: Gunnar Beutner Date: Sun, 26 Oct 2014 18:59:49 +0000 (+0100) Subject: Replace cJSON with YAJL X-Git-Tag: v2.2.0~256 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=755927335989edaf23a8a48ebf6c25a89eb5c5d0;p=icinga2 Replace cJSON with YAJL fixes #7452 --- diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 1c831ba24..8d2606cf2 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -25,7 +25,7 @@ mkclass_target(sysloglogger.ti sysloglogger.thpp) set(base_SOURCES application.cpp application.thpp array.cpp configerror.cpp console.cpp context.cpp convert.cpp debuginfo.cpp dictionary.cpp dynamicobject.cpp dynamicobject.thpp dynamictype.cpp - exception.cpp fifo.cpp filelogger.cpp filelogger.thpp logger.cpp logger.thpp + exception.cpp fifo.cpp filelogger.cpp filelogger.thpp json.cpp logger.cpp logger.thpp netstring.cpp networkstream.cpp object.cpp objectlock.cpp process.cpp ringbuffer.cpp scriptfunction.cpp scriptfunctionwrapper.cpp scriptutils.cpp scriptvariable.cpp serializer.cpp socket.cpp stacktrace.cpp @@ -41,7 +41,7 @@ endif() add_library(base SHARED ${base_SOURCES}) -target_link_libraries(base ${CMAKE_DL_LIBS} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} cJSON mmatch) +target_link_libraries(base ${CMAKE_DL_LIBS} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} mmatch yajl) if(HAVE_LIBEXECINFO) target_link_libraries(base execinfo) @@ -56,6 +56,9 @@ link_directories(${icinga2_BINARY_DIR}/third-party/execvpe) include_directories(${icinga2_SOURCE_DIR}/third-party/mmatch) link_directories(${icinga2_BINARY_DIR}/third-party/mmatch) +include_directories(${icinga2_BINARY_DIR}/third-party/yajl/yajl/include) +link_directories(${icinga2_BINARY_DIR}/third-party/yajl) + if(UNIX OR CYGWIN) target_link_libraries(base execvpe) endif() diff --git a/lib/base/array.cpp b/lib/base/array.cpp index 924eaf279..b352e6ec4 100644 --- a/lib/base/array.cpp +++ b/lib/base/array.cpp @@ -20,7 +20,6 @@ #include "base/array.hpp" #include "base/objectlock.hpp" #include "base/debug.hpp" -#include #include using namespace icinga; @@ -198,46 +197,3 @@ Array::Ptr Array::ShallowClone(void) const CopyTo(clone); return clone; } - -/** - * Converts a JSON object to an array. - * - * @param json The JSON object. - * @returns An array that is equivalent to the JSON object. - */ -Array::Ptr Array::FromJson(cJSON *json) -{ - Array::Ptr array = make_shared(); - - ASSERT(json->type == cJSON_Array); - - for (cJSON *i = json->child; i != NULL; i = i->next) { - array->Add(Value::FromJson(i)); - } - - return array; -} - -/** - * Converts this array to a JSON object. - * - * @returns A JSON object that is equivalent to the array. Values that - * cannot be represented in JSON are omitted. - */ -cJSON *Array::ToJson(void) const -{ - cJSON *json = cJSON_CreateArray(); - - try { - ObjectLock olock(this); - - BOOST_FOREACH(const Value& value, m_Data) { - cJSON_AddItemToArray(json, value.ToJson()); - } - } catch (...) { - cJSON_Delete(json); - throw; - } - - return json; -} diff --git a/lib/base/array.hpp b/lib/base/array.hpp index 5e0602106..bfbea2588 100644 --- a/lib/base/array.hpp +++ b/lib/base/array.hpp @@ -65,9 +65,6 @@ public: void CopyTo(const Array::Ptr& dest) const; Array::Ptr ShallowClone(void) const; - static Array::Ptr FromJson(cJSON *json); - cJSON *ToJson(void) const; - private: std::vector m_Data; /**< The data for the array. */ }; diff --git a/lib/base/dictionary.cpp b/lib/base/dictionary.cpp index 4362a13d7..cec0961a1 100644 --- a/lib/base/dictionary.cpp +++ b/lib/base/dictionary.cpp @@ -20,7 +20,6 @@ #include "base/dictionary.hpp" #include "base/objectlock.hpp" #include "base/debug.hpp" -#include #include using namespace icinga; @@ -213,46 +212,3 @@ Dictionary::Ptr Dictionary::ShallowClone(void) const CopyTo(clone); return clone; } - -/** - * Converts a JSON object to a dictionary. - * - * @param json The JSON object. - * @returns A dictionary that is equivalent to the JSON object. - */ -Dictionary::Ptr Dictionary::FromJson(cJSON *json) -{ - Dictionary::Ptr dictionary = make_shared(); - - ASSERT(json->type == cJSON_Object); - - for (cJSON *i = json->child; i != NULL; i = i->next) { - dictionary->Set(i->string, Value::FromJson(i)); - } - - return dictionary; -} - -/** - * Converts this dictionary to a JSON object. - * - * @returns A JSON object that is equivalent to the dictionary. Values that - * cannot be represented in JSON are omitted. - */ -cJSON *Dictionary::ToJson(void) const -{ - cJSON *json = cJSON_CreateObject(); - - try { - ObjectLock olock(this); - - BOOST_FOREACH(const Dictionary::Pair& kv, m_Data) { - cJSON_AddItemToObject(json, kv.first.CStr(), kv.second.ToJson()); - } - } catch (...) { - cJSON_Delete(json); - throw; - } - - return json; -} diff --git a/lib/base/dictionary.hpp b/lib/base/dictionary.hpp index ccce691b6..754182b1c 100644 --- a/lib/base/dictionary.hpp +++ b/lib/base/dictionary.hpp @@ -64,9 +64,6 @@ public: void CopyTo(const Dictionary::Ptr& dest) const; Dictionary::Ptr ShallowClone(void) const; - static Dictionary::Ptr FromJson(cJSON *json); - cJSON *ToJson(void) const; - private: std::map m_Data; /**< The data for the dictionary. */ }; diff --git a/lib/base/dynamicobject.cpp b/lib/base/dynamicobject.cpp index 919ee0599..0d5d0d8b6 100644 --- a/lib/base/dynamicobject.cpp +++ b/lib/base/dynamicobject.cpp @@ -21,6 +21,7 @@ #include "base/dynamictype.hpp" #include "base/serializer.hpp" #include "base/netstring.hpp" +#include "base/json.hpp" #include "base/stdiostream.hpp" #include "base/debug.hpp" #include "base/objectlock.hpp" @@ -266,7 +267,7 @@ void DynamicObject::DumpObjects(const String& filename, int attributeTypes) persistentObject->Set("update", update); - String json = JsonSerialize(persistentObject); + String json = JsonEncode(persistentObject); NetString::WriteStringToStream(sfp, json); } @@ -290,7 +291,7 @@ void DynamicObject::DumpObjects(const String& filename, int attributeTypes) void DynamicObject::RestoreObject(const String& message, int attributeTypes) { - Dictionary::Ptr persistentObject = JsonDeserialize(message); + Dictionary::Ptr persistentObject = JsonDecode(message); String type = persistentObject->Get("type"); diff --git a/lib/base/dynamicobject.hpp b/lib/base/dynamicobject.hpp index 1b64b0d2c..68611f89a 100644 --- a/lib/base/dynamicobject.hpp +++ b/lib/base/dynamicobject.hpp @@ -23,7 +23,7 @@ #include "base/i2-base.hpp" #include "base/dynamicobject.thpp" #include "base/object.hpp" -#include "base/serializer.hpp" +#include "base/type.hpp" #include "base/dictionary.hpp" #include "base/debuginfo.hpp" #include diff --git a/lib/base/json.cpp b/lib/base/json.cpp new file mode 100644 index 000000000..cd5d30d5a --- /dev/null +++ b/lib/base/json.cpp @@ -0,0 +1,338 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * + * * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "base/json.hpp" +#include "base/debug.hpp" +#include "base/dictionary.hpp" +#include "base/array.hpp" +#include "base/objectlock.hpp" +#include "base/convert.hpp" +#include +#include +#include +#include +#include + +using namespace icinga; + +static void Encode(yajl_gen handle, const Value& value); + +static void EncodeDictionary(yajl_gen handle, const Dictionary::Ptr& dict) +{ + yajl_gen_map_open(handle); + + ObjectLock olock(dict); + BOOST_FOREACH(const Dictionary::Pair& kv, dict) { + yajl_gen_string(handle, reinterpret_cast(kv.first.CStr()), kv.first.GetLength()); + Encode(handle, kv.second); + } + + yajl_gen_map_close(handle); +} + +static void EncodeArray(yajl_gen handle, const Array::Ptr& arr) +{ + yajl_gen_array_open(handle); + + ObjectLock olock(arr); + BOOST_FOREACH(const Value& value, arr) { + Encode(handle, value); + } + + yajl_gen_array_close(handle); +} + +static void Encode(yajl_gen handle, const Value& value) +{ + String str; + + switch (value.GetType()) { + case ValueNumber: + if (yajl_gen_double(handle, static_cast(value)) == yajl_gen_invalid_number) + yajl_gen_double(handle, 0); + + break; + case ValueString: + str = value; + yajl_gen_string(handle, reinterpret_cast(str.CStr()), str.GetLength()); + + break; + case ValueObject: + if (value.IsObjectType()) + EncodeDictionary(handle, value); + else if (value.IsObjectType()) + EncodeArray(handle, value); + else + yajl_gen_null(handle); + + break; + case ValueEmpty: + yajl_gen_null(handle); + + break; + default: + VERIFY(!"Invalid variant type."); + } +} + +String icinga::JsonEncode(const Value& value) +{ + yajl_gen handle = yajl_gen_alloc(NULL); + Encode(handle, value); + + const unsigned char *buf; + size_t len; + yajl_gen_get_buf(handle, &buf, &len); + + String result = String(buf, buf + len); + + yajl_gen_free(handle); + + return result; +} + +struct JsonElement +{ + String Key; + bool KeySet; + Value Value; + + JsonElement(void) + : KeySet(false) + { } +}; + +struct JsonContext +{ +public: + void Push(const Value& value) + { + JsonElement element; + element.Value = value; + + m_Stack.push(element); + } + + JsonElement Pop(void) + { + JsonElement value = m_Stack.top(); + m_Stack.pop(); + return value; + } + + void AddValue(const Value& value) + { + if (m_Stack.empty()) { + JsonElement element; + element.Value = value; + m_Stack.push(element); + return; + } + + JsonElement& element = m_Stack.top(); + + if (element.Value.IsObjectType()) { + if (!element.KeySet) { + element.Key = value; + element.KeySet = true; + } else { + Dictionary::Ptr dict = element.Value; + dict->Set(element.Key, value); + element.KeySet = false; + } + } else if (element.Value.IsObjectType()) { + Array::Ptr arr = element.Value; + arr->Add(value); + } else { + BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot add value to JSON element.")); + } + } + + Value GetValue(void) const + { + ASSERT(m_Stack.size() == 1); + return m_Stack.top().Value; + } + + void SaveException(void) + { + m_Exception = boost::current_exception(); + } + + void ThrowException(void) const + { + boost::rethrow_exception(m_Exception); + } + +private: + std::stack m_Stack; + Value m_Key; + boost::exception_ptr m_Exception; +}; + +static int DecodeNull(void *ctx) +{ + JsonContext *context = static_cast(ctx); + + try { + context->AddValue(Empty); + } catch (...) { + context->SaveException(); + return 0; + } + + return 1; +} + +static int DecodeBoolean(void *ctx, int value) +{ + JsonContext *context = static_cast(ctx); + + try { + context->AddValue(value); + } catch (...) { + context->SaveException(); + return 0; + } + + return 1; +} + +static int DecodeNumber(void *ctx, const char *str, size_t len) +{ + JsonContext *context = static_cast(ctx); + + try { + String jstr = String(str, str + len); + context->AddValue(Convert::ToDouble(jstr)); + } catch (...) { + context->SaveException(); + return 0; + } + + return 1; +} + +static int DecodeString(void *ctx, const unsigned char *str, size_t len) +{ + JsonContext *context = static_cast(ctx); + + try { + context->AddValue(String(reinterpret_cast(str), reinterpret_cast(str) + len)); + } catch (...) { + context->SaveException(); + return 0; + } + + return 1; +} + +static int DecodeStartMap(void *ctx) +{ + JsonContext *context = static_cast(ctx); + + try { + context->Push(make_shared()); + } catch (...) { + context->SaveException(); + return 0; + } + + return 1; +} + +static int DecodeEndMap(void *ctx) +{ + JsonContext *context = static_cast(ctx); + + try { + context->AddValue(context->Pop().Value); + } catch (...) { + context->SaveException(); + return 0; + } + + return 1; +} + +static int DecodeStartArray(void *ctx) +{ + JsonContext *context = static_cast(ctx); + + try { + context->Push(make_shared()); + } catch (...) { + context->SaveException(); + return 0; + } + + return 1; +} + +static int DecodeEndArray(void *ctx) +{ + JsonContext *context = static_cast(ctx); + + try { + context->AddValue(context->Pop().Value); + } catch (...) { + context->SaveException(); + return 0; + } + + return 1; +} + +Value icinga::JsonDecode(const String& data) +{ + static const yajl_callbacks callbacks = { + DecodeNull, + DecodeBoolean, + NULL, + NULL, + DecodeNumber, + DecodeString, + DecodeStartMap, + DecodeString, + DecodeEndMap, + DecodeStartArray, + DecodeEndArray + }; + + yajl_handle handle; + JsonContext context; + + handle = yajl_alloc(&callbacks, NULL, &context); + + yajl_parse(handle, reinterpret_cast(data.CStr()), data.GetLength()); + + if (yajl_complete_parse(handle) != yajl_status_ok) { + unsigned char *internal_err_str = yajl_get_error(handle, 1, reinterpret_cast(data.CStr()), data.GetLength()); + String msg = reinterpret_cast(internal_err_str); + yajl_free_error(handle, internal_err_str); + + yajl_free(handle); + + BOOST_THROW_EXCEPTION(std::invalid_argument(msg)); + } + + yajl_free(handle); + + return context.GetValue(); +} diff --git a/lib/base/json.hpp b/lib/base/json.hpp new file mode 100644 index 000000000..62f37aa28 --- /dev/null +++ b/lib/base/json.hpp @@ -0,0 +1,34 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * + * * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef JSON_H +#define JSON_H + +#include "base/i2-base.hpp" +#include "base/value.hpp" + +namespace icinga +{ + +I2_BASE_API String JsonEncode(const Value& value); +I2_BASE_API Value JsonDecode(const String& data); + +} + +#endif /* JSON_H */ diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp index c24e3ba75..a4573ff66 100644 --- a/lib/base/scriptutils.cpp +++ b/lib/base/scriptutils.cpp @@ -23,7 +23,7 @@ #include "base/convert.hpp" #include "base/array.hpp" #include "base/dictionary.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include "base/logger.hpp" #include #include @@ -135,7 +135,7 @@ void ScriptUtils::Log(const std::vector& arguments) if (message.IsString()) ::Log(severity, facility, message); else - ::Log(severity, facility, JsonSerialize(message)); + ::Log(severity, facility, JsonEncode(message)); } Array::Ptr ScriptUtils::Range(const std::vector& arguments) diff --git a/lib/base/scriptvariable.cpp b/lib/base/scriptvariable.cpp index ef131fb50..f730c1f93 100644 --- a/lib/base/scriptvariable.cpp +++ b/lib/base/scriptvariable.cpp @@ -22,6 +22,7 @@ #include "base/logger.hpp" #include "base/stdiostream.hpp" #include "base/netstring.hpp" +#include "base/json.hpp" #include "base/convert.hpp" #include #include @@ -124,7 +125,7 @@ void ScriptVariable::WriteVariablesFile(const String& filename) persistentVariable->Set("value", value); - String json = JsonSerialize(persistentVariable); + String json = JsonEncode(persistentVariable); NetString::WriteStringToStream(sfp, json); } diff --git a/lib/base/serializer.cpp b/lib/base/serializer.cpp index aee604350..196adc77b 100644 --- a/lib/base/serializer.cpp +++ b/lib/base/serializer.cpp @@ -22,55 +22,9 @@ #include "base/application.hpp" #include "base/objectlock.hpp" #include -#include using namespace icinga; -/** - * Serializes a Value into a JSON string. - * - * @returns A string representing the Value. - */ -String icinga::JsonSerialize(const Value& value) -{ - cJSON *json = value.ToJson(); - - char *jsonString; - -#ifdef _DEBUG - jsonString = cJSON_Print(json); -#else /* _DEBUG */ - jsonString = cJSON_PrintUnformatted(json); -#endif /* _DEBUG */ - - cJSON_Delete(json); - - String result = jsonString; - - free(jsonString); - - return result; -} - -/** - * Deserializes the string representation of a Value. - * - * @param data A JSON string obtained from JsonSerialize - * @returns The newly deserialized Value. - */ -Value icinga::JsonDeserialize(const String& data) -{ - cJSON *json = cJSON_Parse(data.CStr()); - - if (!json) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid JSON String: " + data)); - - Value value = Value::FromJson(json); - cJSON_Delete(json); - - return value; -} - static Array::Ptr SerializeArray(const Array::Ptr& input, int attributeTypes) { Array::Ptr result = make_shared(); diff --git a/lib/base/serializer.hpp b/lib/base/serializer.hpp index ce0f8cae2..ed9cc8616 100644 --- a/lib/base/serializer.hpp +++ b/lib/base/serializer.hpp @@ -21,20 +21,12 @@ #define SERIALIZER_H #include "base/i2-base.hpp" +#include "base/type.hpp" #include "base/value.hpp" namespace icinga { -enum FieldAttribute -{ - FAConfig = 1, - FAState = 2 -}; - -I2_BASE_API String JsonSerialize(const Value& value); -I2_BASE_API Value JsonDeserialize(const String& data); - I2_BASE_API Value Serialize(const Value& value, int attributeTypes = FAState); I2_BASE_API Value Deserialize(const Value& value, bool safe_mode = false, int attributeTypes = FAState); I2_BASE_API Value Deserialize(const Object::Ptr& object, const Value& value, bool safe_mode = false, int attributeTypes = FAState); diff --git a/lib/base/type.hpp b/lib/base/type.hpp index da9baac64..f26b3fcee 100644 --- a/lib/base/type.hpp +++ b/lib/base/type.hpp @@ -30,6 +30,12 @@ namespace icinga { +enum FieldAttribute +{ + FAConfig = 1, + FAState = 2 +}; + struct Field { int ID; diff --git a/lib/base/value.cpp b/lib/base/value.cpp index b4d1984a6..9dff7cee2 100644 --- a/lib/base/value.cpp +++ b/lib/base/value.cpp @@ -21,7 +21,6 @@ #include "base/array.hpp" #include "base/dictionary.hpp" #include "base/type.hpp" -#include using namespace icinga; @@ -137,65 +136,6 @@ bool Value::ToBool(void) const } } - -/** - * Converts a JSON object into a variant. - * - * @param json The JSON object. - */ -Value Value::FromJson(cJSON *json) -{ - if (json->type == cJSON_Number) - return json->valuedouble; - else if (json->type == cJSON_String) - return String(json->valuestring); - else if (json->type == cJSON_True) - return 1; - else if (json->type == cJSON_False) - return 0; - else if (json->type == cJSON_Object) - return Dictionary::FromJson(json); - else if (json->type == cJSON_Array) - return Array::FromJson(json); - else if (json->type == cJSON_NULL) - return Value(); - else - BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported JSON type.")); -} - -/** - * Serializes the variant. - * - * @returns A JSON object representing this variant. - */ -cJSON *Value::ToJson(void) const -{ - switch (GetType()) { - case ValueNumber: - return cJSON_CreateNumber(boost::get(m_Value)); - - case ValueString: - return cJSON_CreateString(boost::get(m_Value).CStr()); - - case ValueObject: - if (IsObjectType()) { - Dictionary::Ptr dictionary = *this; - return dictionary->ToJson(); - } else if (IsObjectType()) { - Array::Ptr array = *this; - return array->ToJson(); - } else { - return cJSON_CreateNull(); - } - - case ValueEmpty: - return cJSON_CreateNull(); - - default: - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid variant type.")); - } -} - /** * Returns the type of the value. * diff --git a/lib/base/value.hpp b/lib/base/value.hpp index 375e7b2da..e2ce64e59 100644 --- a/lib/base/value.hpp +++ b/lib/base/value.hpp @@ -126,9 +126,6 @@ public: return (dynamic_pointer_cast(boost::get(m_Value)) != NULL); } - static Value FromJson(cJSON *json); - cJSON *ToJson(void) const; - ValueType GetType(void) const; String GetTypeName(void) const; diff --git a/lib/cli/agentutility.cpp b/lib/cli/agentutility.cpp index f2647a393..94ac0d17d 100644 --- a/lib/cli/agentutility.cpp +++ b/lib/cli/agentutility.cpp @@ -23,7 +23,7 @@ #include "base/application.hpp" #include "base/tlsutility.hpp" #include "base/convert.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include "base/netstring.hpp" #include "base/stdiostream.hpp" #include "base/debug.hpp" @@ -112,7 +112,7 @@ void AgentUtility::PrintAgentsJson(std::ostream& fp) result->Set(agent->Get("endpoint"), agent); } - fp << JsonSerialize(result); + fp << JsonEncode(result); } bool AgentUtility::AddAgent(const String& name) @@ -206,7 +206,7 @@ bool AgentUtility::WriteAgentToRepository(const String& filename, const Dictiona String tempFilename = filename + ".tmp"; std::ofstream fp(tempFilename.CStr(), std::ofstream::out | std::ostream::trunc); - fp << JsonSerialize(item); + fp << JsonEncode(item); fp.close(); #ifdef _WIN32 @@ -235,7 +235,7 @@ Dictionary::Ptr AgentUtility::GetAgentFromRepository(const String& filename) fp.close(); - return JsonDeserialize(content); + return JsonDecode(content); } std::vector AgentUtility::GetAgents(void) diff --git a/lib/cli/objectlistcommand.cpp b/lib/cli/objectlistcommand.cpp index 63e1e7b53..28c4a4652 100644 --- a/lib/cli/objectlistcommand.cpp +++ b/lib/cli/objectlistcommand.cpp @@ -23,7 +23,7 @@ #include "base/convert.hpp" #include "base/dynamicobject.hpp" #include "base/dynamictype.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include "base/netstring.hpp" #include "base/stdiostream.hpp" #include "base/debug.hpp" @@ -116,7 +116,7 @@ int ObjectListCommand::Run(const boost::program_options::variables_map& vm, cons void ObjectListCommand::PrintObject(std::ostream& fp, bool& first, const String& message, std::map& type_count, const String& name_filter, const String& type_filter) { - Dictionary::Ptr object = JsonDeserialize(message); + Dictionary::Ptr object = JsonDecode(message); Dictionary::Ptr properties = object->Get("properties"); diff --git a/lib/cli/repositoryutility.cpp b/lib/cli/repositoryutility.cpp index fef05aa8a..3b9558c46 100644 --- a/lib/cli/repositoryutility.cpp +++ b/lib/cli/repositoryutility.cpp @@ -22,7 +22,7 @@ #include "base/logger.hpp" #include "base/application.hpp" #include "base/convert.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include "base/netstring.hpp" #include "base/stdiostream.hpp" #include "base/debug.hpp" @@ -101,7 +101,7 @@ void RepositoryUtility::PrintObjects(std::ostream& fp, const String& type) if (obj) { fp << "Object Name: " << object << "\n"; - fp << JsonSerialize(obj); + fp << JsonEncode(obj); } } } @@ -157,7 +157,7 @@ void RepositoryUtility::PrintChangeLog(std::ostream& fp) std::cout << "Changes to be committed:\n"; BOOST_FOREACH(const Value& entry, changelog) { - std::cout << JsonSerialize(entry) << "\n"; //TODO better formatting + std::cout << JsonEncode(entry) << "\n"; //TODO better formatting } } @@ -267,7 +267,7 @@ bool RepositoryUtility::WriteObjectToRepositoryChangeLog(const String& path, con String tempPath = path + ".tmp"; std::ofstream fp(tempPath.CStr(), std::ofstream::out | std::ostream::trunc); - fp << JsonSerialize(item); + fp << JsonEncode(item); fp.close(); #ifdef _WIN32 @@ -296,7 +296,7 @@ Dictionary::Ptr RepositoryUtility::GetObjectFromRepositoryChangeLog(const String fp.close(); - return JsonDeserialize(content); + return JsonDecode(content); } /* diff --git a/lib/cli/variablegetcommand.cpp b/lib/cli/variablegetcommand.cpp index 82d43af2f..d204f550f 100644 --- a/lib/cli/variablegetcommand.cpp +++ b/lib/cli/variablegetcommand.cpp @@ -23,7 +23,7 @@ #include "base/convert.hpp" #include "base/dynamicobject.hpp" #include "base/dynamictype.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include "base/netstring.hpp" #include "base/stdiostream.hpp" #include "base/debug.hpp" @@ -92,7 +92,7 @@ int VariableGetCommand::Run(const boost::program_options::variables_map& vm, con String message; while (NetString::ReadStringFromStream(sfp, &message)) { - Dictionary::Ptr variable = JsonDeserialize(message); + Dictionary::Ptr variable = JsonDecode(message); if (variable->Get("name") == ap[0]) { std::cout << variable->Get("value") << "\n"; diff --git a/lib/cli/variablelistcommand.cpp b/lib/cli/variablelistcommand.cpp index 3deb62fc1..e3c0bbfe1 100644 --- a/lib/cli/variablelistcommand.cpp +++ b/lib/cli/variablelistcommand.cpp @@ -23,7 +23,7 @@ #include "base/convert.hpp" #include "base/dynamicobject.hpp" #include "base/dynamictype.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include "base/netstring.hpp" #include "base/stdiostream.hpp" #include "base/debug.hpp" @@ -90,7 +90,7 @@ int VariableListCommand::Run(const boost::program_options::variables_map& vm, co void VariableListCommand::PrintVariable(std::ostream& fp, const String& message) { - Dictionary::Ptr variable = JsonDeserialize(message); + Dictionary::Ptr variable = JsonDecode(message); std::cout << variable->Get("name") << " = " << variable->Get("value") << "\n"; } diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index 7dd1a7816..67b288ba8 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -32,6 +32,7 @@ #include "base/exception.hpp" #include "base/stdiostream.hpp" #include "base/netstring.hpp" +#include "base/json.hpp" #include "base/configerror.hpp" #include #include @@ -310,7 +311,7 @@ void ConfigItem::WriteObjectsFile(const String& filename) } persistentItem->Set("debug_hints", item->GetDebugHints()); - String json = JsonSerialize(persistentItem); + String json = JsonEncode(persistentItem); NetString::WriteStringToStream(sfp, json); } diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index 3e0b19572..a8d4513c9 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -23,7 +23,7 @@ #include "config/applyrule.hpp" #include "config/objectrule.hpp" #include "base/array.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include "base/scriptfunction.hpp" #include "base/scriptvariable.hpp" #include "base/utility.hpp" @@ -84,7 +84,7 @@ void Expression::DumpOperand(std::ostream& stream, const Value& operand, int ind Expression::Ptr left = operand; left->Dump(stream, indent); } else { - stream << String(indent, ' ') << JsonSerialize(operand) << "\n"; + stream << String(indent, ' ') << JsonEncode(operand) << "\n"; } } @@ -215,7 +215,7 @@ Value Expression::OpIn(const Expression *expr, const Dictionary::Ptr& locals, De if (right.IsEmpty()) return false; else if (!right.IsObjectType()) - BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonSerialize(right))); + BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonEncode(right))); Value left = expr->EvaluateOperand1(locals); diff --git a/lib/icinga/apievents.cpp b/lib/icinga/apievents.cpp index c9aa173f3..c4fe50983 100644 --- a/lib/icinga/apievents.cpp +++ b/lib/icinga/apievents.cpp @@ -29,6 +29,8 @@ #include "base/utility.hpp" #include "base/exception.hpp" #include "base/initialize.hpp" +#include "base/serializer.hpp" +#include "base/json.hpp" #include using namespace icinga; @@ -1540,7 +1542,7 @@ Value ApiEvents::UpdateRepositoryAPIHandler(const MessageOrigin& origin, const D String repositoryTempFile = repositoryFile + ".tmp"; std::ofstream fp(repositoryTempFile.CStr(), std::ofstream::out | std::ostream::trunc); - fp << JsonSerialize(params); + fp << JsonEncode(params); fp.close(); #ifdef _WIN32 diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 52cdd7658..b21cc3f4d 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -25,7 +25,7 @@ #include "base/convert.hpp" #include "base/utility.hpp" #include "base/debug.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include using namespace icinga; @@ -123,7 +123,7 @@ Service::Ptr Host::GetServiceByShortName(const Value& name) return Service::GetByNamePair(dict->Get("host"), dict->Get("service")); } else { - BOOST_THROW_EXCEPTION(std::invalid_argument("Host/Service name pair is invalid: " + JsonSerialize(name))); + BOOST_THROW_EXCEPTION(std::invalid_argument("Host/Service name pair is invalid: " + JsonEncode(name))); } } diff --git a/lib/icinga/icingastatuswriter.cpp b/lib/icinga/icingastatuswriter.cpp index ac4948131..9f10b7d79 100644 --- a/lib/icinga/icingastatuswriter.cpp +++ b/lib/icinga/icingastatuswriter.cpp @@ -24,6 +24,7 @@ #include "base/exception.hpp" #include "base/application.hpp" #include "base/statsfunction.hpp" +#include "base/json.hpp" #include #include #include @@ -151,7 +152,7 @@ void IcingaStatusWriter::StatusTimerHandler(void) statusfp << std::fixed; - statusfp << JsonSerialize(GetStatusData()); + statusfp << JsonEncode(GetStatusData()); statusfp.close(); diff --git a/lib/livestatus/livestatusquery.cpp b/lib/livestatus/livestatusquery.cpp index 44a522cfd..0b3e5dec1 100644 --- a/lib/livestatus/livestatusquery.cpp +++ b/lib/livestatus/livestatusquery.cpp @@ -37,7 +37,7 @@ #include "base/logger.hpp" #include "base/exception.hpp" #include "base/utility.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include #include #include @@ -382,7 +382,7 @@ void LivestatusQuery::PrintResultSet(std::ostream& fp, const Array::Ptr& rs) con fp << m_Separators[0]; } } else if (m_OutputFormat == "json") { - fp << JsonSerialize(rs); + fp << JsonEncode(rs); } else if (m_OutputFormat == "python") { PrintPythonArray(fp, rs); } diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index 5dc67dc7f..da204dd96 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -22,6 +22,7 @@ #include "remote/endpoint.hpp" #include "base/convert.hpp" #include "base/netstring.hpp" +#include "base/json.hpp" #include "base/dynamictype.hpp" #include "base/logger.hpp" #include "base/objectlock.hpp" @@ -461,7 +462,7 @@ void ApiListener::PersistMessage(const Dictionary::Ptr& message, const DynamicOb Dictionary::Ptr pmessage = make_shared(); pmessage->Set("timestamp", ts); - pmessage->Set("message", JsonSerialize(message)); + pmessage->Set("message", JsonEncode(message)); Dictionary::Ptr secname = make_shared(); secname->Set("type", secobj->GetType()->GetName()); @@ -470,7 +471,7 @@ void ApiListener::PersistMessage(const Dictionary::Ptr& message, const DynamicOb boost::mutex::scoped_lock lock(m_LogLock); if (m_LogFile) { - NetString::WriteStringToStream(m_LogFile, JsonSerialize(pmessage)); + NetString::WriteStringToStream(m_LogFile, JsonEncode(pmessage)); m_LogMessageCount++; SetLogMessageTimestamp(ts); @@ -683,7 +684,7 @@ void ApiListener::ReplayLog(const ApiClient::Ptr& client) if (!NetString::ReadStringFromStream(logStream, &message)) break; - pmessage = JsonDeserialize(message); + pmessage = JsonDecode(message); } catch (const std::exception&) { Log(LogWarning, "ApiListener") << "Unexpected end-of-file for cluster log: " << path; diff --git a/lib/remote/jsonrpc.cpp b/lib/remote/jsonrpc.cpp index 1ee32d703..bee339e1e 100644 --- a/lib/remote/jsonrpc.cpp +++ b/lib/remote/jsonrpc.cpp @@ -19,7 +19,7 @@ #include "remote/jsonrpc.hpp" #include "base/netstring.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" //#include using namespace icinga; @@ -31,7 +31,7 @@ using namespace icinga; */ void JsonRpc::SendMessage(const Stream::Ptr& stream, const Dictionary::Ptr& message) { - String json = JsonSerialize(message); + String json = JsonEncode(message); //std::cerr << ">> " << json << std::endl; NetString::WriteStringToStream(stream, json); } @@ -43,7 +43,7 @@ Dictionary::Ptr JsonRpc::ReadMessage(const Stream::Ptr& stream) return Dictionary::Ptr(); //std::cerr << "<< " << jsonString << std::endl; - Value value = JsonDeserialize(jsonString); + Value value = JsonDecode(jsonString); if (!value.IsObjectType()) { BOOST_THROW_EXCEPTION(std::invalid_argument("JSON-RPC" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fc331fcac..d2e158b52 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,10 +19,10 @@ include(BoostTestTargets) set(base_test_SOURCES base-array.cpp base-convert.cpp base-dictionary.cpp base-fifo.cpp - base-match.cpp base-netstring.cpp base-object.cpp base-serialize.cpp - base-shellescape.cpp base-stacktrace.cpp base-stream.cpp - base-string.cpp base-timer.cpp base-type.cpp base-value.cpp - icinga-perfdata.cpp test.cpp + base-json.cpp base-match.cpp base-netstring.cpp base-object.cpp + base-serialize.cpp base-shellescape.cpp base-stacktrace.cpp + base-stream.cpp base-string.cpp base-timer.cpp base-type.cpp + base-value.cpp icinga-perfdata.cpp test.cpp ) set_property(SOURCE test.cpp PROPERTY EXCLUDE_UNITY_BUILD TRUE) @@ -54,6 +54,7 @@ add_boost_test(base base_dictionary/json base_fifo/construct base_fifo/io + base_json/invalid1 base_match/tolong base_netstring/netstring base_object/construct diff --git a/test/base-array.cpp b/test/base-array.cpp index da84eebdd..e58cc9e21 100644 --- a/test/base-array.cpp +++ b/test/base-array.cpp @@ -19,7 +19,7 @@ #include "base/array.hpp" #include "base/objectlock.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include #include @@ -133,10 +133,10 @@ BOOST_AUTO_TEST_CASE(json) array->Add(2); array->Add(5); - String json = JsonSerialize(array); + String json = JsonEncode(array); BOOST_CHECK(json.GetLength() > 0); - Array::Ptr deserialized = JsonDeserialize(json); + Array::Ptr deserialized = JsonDecode(json); BOOST_CHECK(deserialized); BOOST_CHECK(deserialized->GetLength() == 3); BOOST_CHECK(deserialized->Get(0) == 7); diff --git a/test/base-dictionary.cpp b/test/base-dictionary.cpp index eed1229c6..f140d5a2a 100644 --- a/test/base-dictionary.cpp +++ b/test/base-dictionary.cpp @@ -19,7 +19,7 @@ #include "base/dictionary.hpp" #include "base/objectlock.hpp" -#include "base/serializer.hpp" +#include "base/json.hpp" #include #include #include @@ -172,9 +172,9 @@ BOOST_AUTO_TEST_CASE(json) dictionary->Set("test1", 7); dictionary->Set("test2", "hello world"); - String json = JsonSerialize(dictionary); + String json = JsonEncode(dictionary); BOOST_CHECK(json.GetLength() > 0); - Dictionary::Ptr deserialized = JsonDeserialize(json); + Dictionary::Ptr deserialized = JsonDecode(json); BOOST_CHECK(deserialized->GetLength() == 2); BOOST_CHECK(deserialized->Get("test1") == 7); BOOST_CHECK(deserialized->Get("test2") == "hello world"); diff --git a/test/base-json.cpp b/test/base-json.cpp new file mode 100644 index 000000000..acc1a6dd8 --- /dev/null +++ b/test/base-json.cpp @@ -0,0 +1,39 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * + * * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "icinga/perfdatavalue.hpp" +#include "base/dictionary.hpp" +#include "base/objectlock.hpp" +#include "base/json.hpp" +#include +#include +#include + +using namespace icinga; + +BOOST_AUTO_TEST_SUITE(base_json) + +BOOST_AUTO_TEST_CASE(invalid1) +{ + BOOST_CHECK_THROW(JsonDecode("\"1.7"), std::exception); + BOOST_CHECK_THROW(JsonDecode("{8: \"test\"}"), std::exception); + BOOST_CHECK_THROW(JsonDecode("{\"test\": \"test\""), std::exception); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index a10b48191..41fc705c5 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -15,8 +15,8 @@ # along with this program; if not, write to the Free Software Foundation # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -add_subdirectory(cJSON) add_subdirectory(mmatch) +add_subdirectory(yajl) if(UNIX OR CYGWIN) add_subdirectory(execvpe) diff --git a/third-party/cJSON/CMakeLists.txt b/third-party/cJSON/CMakeLists.txt deleted file mode 100644 index 617a9e179..000000000 --- a/third-party/cJSON/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -# Icinga 2 -# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) -# -# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - -add_library(cJSON SHARED cJSON.c cJSON.h) - -set_target_properties ( - cJSON PROPERTIES - DEFINE_SYMBOL I2_CJSON_BUILD - FOLDER Lib -) - -if(UNIX AND NOT HAIKU) - target_link_libraries(cJSON m) -endif() - -install( - TARGETS cJSON - RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 -) diff --git a/third-party/cJSON/cJSON.c b/third-party/cJSON/cJSON.c deleted file mode 100644 index f1bef65ff..000000000 --- a/third-party/cJSON/cJSON.c +++ /dev/null @@ -1,522 +0,0 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -/* cJSON */ -/* JSON parser in C. */ - -#include -#include -#include -#include -#include -#include -#include -#include "cJSON.h" - -static const char *ep; - -const char *cJSON_GetErrorPtr() {return ep;} - -static int cJSON_strcasecmp(const char *s1,const char *s2) -{ - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; - for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); -} - -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; - -static char* cJSON_strdup(const char* str) -{ - size_t len; - char* copy; - - len = strlen(str) + 1; - if (!(copy = (char*)cJSON_malloc(len))) return 0; - memcpy(copy,str,len); - return copy; -} - -void cJSON_InitHooks(cJSON_Hooks* hooks) -{ - if (!hooks) { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; - return; - } - - cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; - cJSON_free = (hooks->free_fn)?hooks->free_fn:free; -} - -/* Internal constructor. */ -static cJSON *cJSON_New_Item() -{ - cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); - if (node) memset(node,0,sizeof(cJSON)); - return node; -} - -/* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) -{ - cJSON *next; - while (c) - { - next=c->next; - if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); - if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); - if (c->string) cJSON_free(c->string); - cJSON_free(c); - c=next; - } -} - -/* Parse the input text to generate a number, and populate the result into item. */ -static const char *parse_number(cJSON *item,const char *num) -{ - double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; - - /* Could use sscanf for this? */ - if (*num=='-') sign=-1,num++; /* Has sign? */ - if (*num=='0') num++; /* is zero */ - if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ - if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ - if (*num=='e' || *num=='E') /* Exponent? */ - { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ - while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ - } - - n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ - - item->valuedouble=n; - item->valueint=(int)n; - item->type=cJSON_Number; - return num; -} - -/* Render the number nicely from the given item into a string. */ -static char *print_number(cJSON *item) -{ - char *str; - double d=item->valuedouble; - if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) - { - str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ - if (str) sprintf(str,"%d",item->valueint); - } - else - { - if (d != d) - { - str=(char*)cJSON_malloc(2); - if (str) - strcpy(str, "0"); - } - else - { - str = (char*)cJSON_malloc(64 + (int)log10(fabs(d))); /* This is a nice tradeoff. */ - if (str) - { - if (fabs(floor(d) - d) <= DBL_EPSILON) sprintf(str, "%.0f", d); - else sprintf(str, "%.*e", (int)log10(d) + 6, d); - } - } - } - return str; -} - -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; -static const char *parse_string(cJSON *item,const char *str) -{ - const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; - if (*str!='\"') {ep=str;return 0;} /* not a string! */ - - while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ - - out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ - if (!out) return 0; - - ptr=str+1;ptr2=out; - while (*ptr!='\"' && *ptr) - { - if (*ptr!='\\') *ptr2++=*ptr++; - else - { - ptr++; - switch (*ptr) - { - case 'b': *ptr2++='\b'; break; - case 'f': *ptr2++='\f'; break; - case 'n': *ptr2++='\n'; break; - case 'r': *ptr2++='\r'; break; - case 't': *ptr2++='\t'; break; - case 'u': /* transcode utf16 to utf8. */ - sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */ - - if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid. - - if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs. - { - if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate. - sscanf(ptr+3,"%4x",&uc2);ptr+=6; - if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate. - uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF); - } - - len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; - - switch (len) { - case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 1: *--ptr2 =(uc | firstByteMark[len]); - } - ptr2+=len; - break; - default: *ptr2++=*ptr; break; - } - ptr++; - } - } - *ptr2=0; - if (*ptr=='\"') ptr++; - item->valuestring=out; - item->type=cJSON_String; - return ptr; -} - -/* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const char *str) -{ - const char *ptr;char *ptr2,*out;int len=0;unsigned char token; - - if (!str) return cJSON_strdup(""); - ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} - - out=(char*)cJSON_malloc(len+3); - if (!out) return 0; - - ptr2=out;ptr=str; - *ptr2++='\"'; - while (*ptr) - { - if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; - else - { - *ptr2++='\\'; - switch (token=*ptr++) - { - case '\\': *ptr2++='\\'; break; - case '\"': *ptr2++='\"'; break; - case '\b': *ptr2++='b'; break; - case '\f': *ptr2++='f'; break; - case '\n': *ptr2++='n'; break; - case '\r': *ptr2++='r'; break; - case '\t': *ptr2++='t'; break; - default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ - } - } - } - *ptr2++='\"';*ptr2++=0; - return out; -} -/* Invote print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} - -/* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item,const char *value); -static char *print_value(cJSON *item,int depth,int fmt); -static const char *parse_array(cJSON *item,const char *value); -static char *print_array(cJSON *item,int depth,int fmt); -static const char *parse_object(cJSON *item,const char *value); -static char *print_object(cJSON *item,int depth,int fmt); - -/* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} - -/* Parse an object - create a new root, and populate. */ -cJSON *cJSON_Parse(const char *value) -{ - cJSON *c=cJSON_New_Item(); - ep=0; - if (!c) return 0; /* memory fail */ - - if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} - return c; -} - -/* Render a cJSON item/entity/structure to text. */ -char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} -char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} - -/* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item,const char *value) -{ - if (!value) return 0; /* Fail on null. */ - if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } - if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } - if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } - if (*value=='\"') { return parse_string(item,value); } - if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } - if (*value=='[') { return parse_array(item,value); } - if (*value=='{') { return parse_object(item,value); } - - ep=value;return 0; /* failure. */ -} - -/* Render a value to text. */ -static char *print_value(cJSON *item,int depth,int fmt) -{ - char *out=0; - if (!item) return 0; - switch ((item->type)&255) - { - case cJSON_NULL: out=cJSON_strdup("null"); break; - case cJSON_False: out=cJSON_strdup("false");break; - case cJSON_True: out=cJSON_strdup("true"); break; - case cJSON_Number: out=print_number(item);break; - case cJSON_String: out=print_string(item);break; - case cJSON_Array: out=print_array(item,depth,fmt);break; - case cJSON_Object: out=print_object(item,depth,fmt);break; - } - return out; -} - -/* Build an array from input text. */ -static const char *parse_array(cJSON *item,const char *value) -{ - cJSON *child; - if (*value!='[') {ep=value;return 0;} /* not an array! */ - - item->type=cJSON_Array; - value=skip(value+1); - if (*value==']') return value+1; /* empty array. */ - - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; /* memory fail */ - value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_value(child,skip(value+1))); - if (!value) return 0; /* memory fail */ - } - - if (*value==']') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ -} - -/* Render an array to text */ -static char *print_array(cJSON *item,int depth,int fmt) -{ - char **entries; - char *out=0,*ptr,*ret;int len=5; - cJSON *child=item->child; - int numentries=0,i=0,fail=0; - - /* How many entries in the array? */ - while (child) numentries++,child=child->next; - /* Allocate an array to hold the values for each */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - memset(entries,0,numentries*sizeof(char*)); - /* Retrieve all the results: */ - child=item->child; - while (child && !fail) - { - ret=print_value(child,depth+1,fmt); - entries[i++]=ret; - if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; - child=child->next; - } - - /* If we didn't fail, try to malloc the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - /* If that fails, we fail. */ - if (!out) fail=1; - - /* Handle failure. */ - if (fail) - { - for (i=0;itype=cJSON_Object; - value=skip(value+1); - if (*value=='}') return value+1; /* empty array. */ - - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; - value=skip(parse_string(child,skip(value))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_string(child,skip(value+1))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; - } - - if (*value=='}') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ -} - -/* Render an object to text. */ -static char *print_object(cJSON *item,int depth,int fmt) -{ - char **entries=0,**names=0; - char *out=0,*ptr,*ret,*str;int len=7,i=0,j; - cJSON *child=item->child; - int numentries=0,fail=0; - /* Count the number of entries. */ - while (child) numentries++,child=child->next; - /* Allocate space for the names and the objects */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - names=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!names) {cJSON_free(entries);return 0;} - memset(entries,0,sizeof(char*)*numentries); - memset(names,0,sizeof(char*)*numentries); - - /* Collect all the results into our arrays: */ - child=item->child;depth++;if (fmt) len+=depth; - while (child) - { - names[i]=str=print_string_ptr(child->string); - entries[i++]=ret=print_value(child,depth,fmt); - if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; - child=child->next; - } - - /* Try to allocate the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - if (!out) fail=1; - - /* Handle failure */ - if (fail) - { - for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} -cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} -cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} - -/* Utility for array list handling. */ -static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} -/* Utility for handling references. */ -static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} - -/* Add item to array/object. */ -void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} -void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} -void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} - -cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; - if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} -void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} -cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} -void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} - -/* Replace array/object items with new ones. */ -void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; - newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; - if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} -void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} - -/* Create basic types: */ -cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} -cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} -cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} -cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} -cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} -cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} -cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} -cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} - -/* Create Arrays: */ -cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} diff --git a/third-party/cJSON/cJSON.h b/third-party/cJSON/cJSON.h deleted file mode 100644 index 5d0555aff..000000000 --- a/third-party/cJSON/cJSON.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#ifndef cJSON__h -#define cJSON__h - -#include "base/visibility.hpp" - -#ifdef I2_CJSON_BUILD -# define I2_CJSON_API I2_EXPORT -#else -# define I2_CJSON_API I2_IMPORT -#endif /* I2_CJSON_BUILD */ - -#ifdef __cplusplus -extern "C" -{ -#endif - -/* cJSON Types: */ -#define cJSON_False 0 -#define cJSON_True 1 -#define cJSON_NULL 2 -#define cJSON_Number 3 -#define cJSON_String 4 -#define cJSON_Array 5 -#define cJSON_Object 6 - -#define cJSON_IsReference 256 - -/* The cJSON structure: */ -typedef struct cJSON { - struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ - - int type; /* The type of the item, as above. */ - - char *valuestring; /* The item's string, if type==cJSON_String */ - int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; /* The item's number, if type==cJSON_Number */ - - char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ -} cJSON; - -typedef struct cJSON_Hooks { - void *(*malloc_fn)(size_t sz); - void (*free_fn)(void *ptr); -} cJSON_Hooks; - -/* Supply malloc, realloc and free functions to cJSON */ -extern I2_CJSON_API void cJSON_InitHooks(cJSON_Hooks* hooks); - - -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ -extern I2_CJSON_API cJSON *cJSON_Parse(const char *value); -/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ -extern I2_CJSON_API char *cJSON_Print(cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ -extern I2_CJSON_API char *cJSON_PrintUnformatted(cJSON *item); -/* Delete a cJSON entity and all subentities. */ -extern I2_CJSON_API void cJSON_Delete(cJSON *c); - -/* Returns the number of items in an array (or object). */ -extern I2_CJSON_API int cJSON_GetArraySize(cJSON *array); -/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ -extern I2_CJSON_API cJSON *cJSON_GetArrayItem(cJSON *array,int item); -/* Get item "string" from object. Case insensitive. */ -extern I2_CJSON_API cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); - -/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -extern I2_CJSON_API const char *cJSON_GetErrorPtr(); - -/* These calls create a cJSON item of the appropriate type. */ -extern I2_CJSON_API cJSON *cJSON_CreateNull(); -extern I2_CJSON_API cJSON *cJSON_CreateTrue(); -extern I2_CJSON_API cJSON *cJSON_CreateFalse(); -extern I2_CJSON_API cJSON *cJSON_CreateBool(int b); -extern I2_CJSON_API cJSON *cJSON_CreateNumber(double num); -extern I2_CJSON_API cJSON *cJSON_CreateString(const char *string); -extern I2_CJSON_API cJSON *cJSON_CreateArray(); -extern I2_CJSON_API cJSON *cJSON_CreateObject(); - -/* These utilities create an Array of count items. */ -extern I2_CJSON_API cJSON *cJSON_CreateIntArray(int *numbers,int count); -extern I2_CJSON_API cJSON *cJSON_CreateFloatArray(float *numbers,int count); -extern I2_CJSON_API cJSON *cJSON_CreateDoubleArray(double *numbers,int count); -extern I2_CJSON_API cJSON *cJSON_CreateStringArray(const char **strings,int count); - -/* Append item to the specified array/object. */ -extern I2_CJSON_API void cJSON_AddItemToArray(cJSON *array, cJSON *item); -extern I2_CJSON_API void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); -/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -extern I2_CJSON_API void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -extern I2_CJSON_API void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); - -/* Remove/Detatch items from Arrays/Objects. */ -extern I2_CJSON_API cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); -extern I2_CJSON_API void cJSON_DeleteItemFromArray(cJSON *array,int which); -extern I2_CJSON_API cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); -extern I2_CJSON_API void cJSON_DeleteItemFromObject(cJSON *object,const char *string); - -/* Update array items. */ -extern I2_CJSON_API void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); -extern I2_CJSON_API void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); - -#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) -#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) -#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) -#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) -#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/third-party/yajl/.gitignore b/third-party/yajl/.gitignore new file mode 100644 index 000000000..b0aff9afe --- /dev/null +++ b/third-party/yajl/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +Makefile +/build/ diff --git a/third-party/yajl/BUILDING b/third-party/yajl/BUILDING new file mode 100644 index 000000000..8460ed4b4 --- /dev/null +++ b/third-party/yajl/BUILDING @@ -0,0 +1,23 @@ +Short story (If you already have ruby and cmake): + +./configure && make install + +When things go wrong: + +attain CMake (http://www.cmake.org) and ruby (http://ruby-lang.org) and +try again. + +OR, attain CMake and build by hand: + +1. mkdir build +2. cd build +3. cmake .. +4. make +5. build output left in yajl-X.Y.Z + +NOTE: for 64-bit systems where lib64 is used you can pass the cmake + variable LIB_SUFFIX to cause installation into the system's 'lib64' + directory. + +best, +lloyd diff --git a/third-party/yajl/BUILDING.win32 b/third-party/yajl/BUILDING.win32 new file mode 100644 index 000000000..6b35aa451 --- /dev/null +++ b/third-party/yajl/BUILDING.win32 @@ -0,0 +1,27 @@ +YAJL has been successfully built using Visual Studio 8. CMake, a +build file generator, is used to build the software. CMake supports +several different build environments, so you may either build YAJL +using the IDE via the following steps: + +1. acquire cmake (http://www.cmake.org) +2. mkdir build +3. cd build +4. cmake .. +5. devenv YetAnotherJSONParser.sln /project ALL_BUILD /build Release +6. build output is left in build/yajl-X.Y.Z + +Or you can build from the command line using nmake: + +1. Click Start > Programs > Microsoft Visual Studio > Visual Studio +Tools > Visual Studio Command Prompt -- for your version of Visual +Studio, which will open a command prompt. You may verify that the +compiler is in your path by typing "cl /?" at the prompt. +2. cd C:\path\to\yajl\source\ +3. mkdir build +4. cd build +5. cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release .. +6. nmake +7. nmake install + +Earlier versions of visual studio and other build generators haven't +been thoroughly tested, but should work without any major issues. diff --git a/third-party/yajl/CMakeLists.txt b/third-party/yajl/CMakeLists.txt new file mode 100644 index 000000000..7440dab50 --- /dev/null +++ b/third-party/yajl/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (c) 2007-2014, Lloyd Hilaiel +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +PROJECT(YetAnotherJSONParser C) + +SET (YAJL_MAJOR 2) +SET (YAJL_MINOR 1) +SET (YAJL_MICRO 0) + +IF (WIN32) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4") + ADD_DEFINITIONS(-DWIN32) + + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4996 /wd4255 /wd4130 /wd4100 /wd4711") + SET(CMAKE_C_FLAGS_DEBUG "/D DEBUG /Od /Z7") + SET(CMAKE_C_FLAGS_RELEASE "/D NDEBUG /O2") +ELSE (WIN32) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") + IF(CMAKE_COMPILER_IS_GNUCC) + INCLUDE(CheckCCompilerFlag) + CHECK_C_COMPILER_FLAG(-fvisibility=hidden HAVE_GCC_VISIBILITY) + IF(HAVE_GCC_VISIBILITY) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") + ENDIF(HAVE_GCC_VISIBILITY) + ENDIF(CMAKE_COMPILER_IS_GNUCC) + SET(CMAKE_C_FLAGS + "${CMAKE_C_FLAGS} -std=c99 -pedantic -Wpointer-arith -Wno-format-y2k -Wstrict-prototypes -Wmissing-declarations -Wnested-externs -Wextra -Wundef -Wwrite-strings -Wold-style-definition -Wredundant-decls -Wno-unused-parameter -Wno-sign-compare -Wmissing-prototypes") + + SET(CMAKE_C_FLAGS_DEBUG "-DDEBUG") + SET(CMAKE_C_FLAGS_RELEASE "-DNDEBUG -Wuninitialized") +ENDIF (WIN32) + +ADD_SUBDIRECTORY(src) diff --git a/third-party/yajl/COPYING b/third-party/yajl/COPYING new file mode 100644 index 000000000..30be34901 --- /dev/null +++ b/third-party/yajl/COPYING @@ -0,0 +1,13 @@ +Copyright (c) 2007-2014, Lloyd Hilaiel + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/third-party/yajl/ChangeLog b/third-party/yajl/ChangeLog new file mode 100644 index 000000000..343f89469 --- /dev/null +++ b/third-party/yajl/ChangeLog @@ -0,0 +1,189 @@ +2.1.0 + * @nonodename, @patperry - fixed some compiler warnings + * @yep, @emaste - documentation improvements + * @sgravrock - build fix for NetBSD (and whenever sh != bash) + * @rotty, @brimstone3, @lloyd - allow client to reset generator + * @sgravrock - remove bash dependencies + * @lloyd - add api tests + * @rflynn - remove ruby dependency + * @cloderic - nmake install works on windows + * @shahbag - build fix for qnx + * @breese - debugging improvements + * @lloyd - json_verify supports -s flag for stream processing + * @lloyd - json_reformat supports -s flag for stream processing + +2.0.4 + * @jcekstrom - additional checking in integer parsing + * @jcekstrom - fix a bug in yajl_tree that would cause valid json integersto fail to parse + * @plaguemorin - fix a memory leak in yajl_tree (error strings were being leaked) + * @7AC - reset errno + * @ConradIrwin - include flags to reformatter to allow toggling of escape solidus option + +2.0.3 + * John Stamp generation of a pkgconfig file at build time. + * @robzuber bugfix in yajl_tree_get() + * @lloyd - fix for compilation on 64 bit windows + +2.0.2 + * lth fix typos in yajl_tree.h macros YAJL_IS_INTEGER and YAJL_IS_DOUBLE, + contributed by Artem S Vybornov. + * lth add #ifdef __cplusplus wrappers to yajl_tree to allow proper + usage from many populer C++ compilers. + +2.0.1 + * lth generator flag to allow client to specify they want + escaped solidi '/'. issue #28 + * lth crash fix when yajl_parse() is never called. issue #27 + +2.0.0 + * lth YAJL is now ISC licensed: http://en.wikipedia.org/wiki/ISC_license + * lth 20-35% (osx and linux respectively) parsing performance + improvement attained by tweaking string scanning (idea: @michaelrhanson). + * Florian Forster & lth - yajl_tree interface introduced as a higher level + interface to the parser (eats JSON, poops a memory representation) + * lth require a C99 compiler + * lth integers are now represented with long long (64bit+) on all platforms. + * lth size_t now used throughout to represent buffer lengths, so you can + safely manage buffers greater than 4GB. + * gno semantic improvements to yajl's API regarding partial value parsing and + trailing garbage + * lth new configuration mechanism for yajl, see yajl_config() and + yajl_gen_config() + * gno more allocation checking in more places + * gno remove usage of strtol, replace with custom implementation that cares + not about your locale. + * lth yajl_parse_complete renamed to yajl_complete_parse. + * lth add a switch to validate utf8 strings as they are generated. + * lth tests are a lot quieter in their output. + * lth addition of a little in tree performance benchmark, `perftest` in + perf/perftest.c + +1.0.12 + * Conrad Irwin - Parse null bytes correctly + * Mirek Rusin - fix LLVM warnings + * gno - Don't generate numbers for keys. closes #13 + * lth - various win32 fixes, including build documentation improvements + * John Stamp - Don't export private symbols. + * John Stamp - Install yajl_version.h, not the template. + * John Stamp - Don't use -fPIC for static lib. Cmake will automatically add it for the shared. + * lth 0 fix paths embedded in dylib upon installation on osx. closes #11 + +1.0.11 + * lth remove -Wno-missing-field-initializers for greater gcc compat (3.4.6) + +1.0.10 + * Brian Maher - yajl is now buildable without a c++ compiler present + * Brian Maher - fix header installation on OSX with cmake 2.8.0 installed + * lth & vitali - allow builder to specify alternate lib directory + for installation (i.e. lib64) + * Vitali Lovich - yajl version number now programatically accessible + * lth - prevent cmake from embedding rpaths in binaries. Static linking + makes this unneccesary. + +1.0.9 + * lth - fix inverted logic causing yajl_gen_double() to always fail on + win32 (thanks to Fredrik Kihlander for the report) + +1.0.8 + * Randall E. Barker - move dllexport defnitions so dlls with proper + exports can again be generated on windows + * lth - add yajl_get_bytes_consumed() which allows the client to + determine the offset as an error, as well as determine how + many bytes of an input buffer were consumed. + * lth - fixes to keep "error offset" up to date (like when the + client callback returns 0) + * Brian Maher - allow client to specify a printing function in + generation + +1.0.7 + * lth fix win32 build (isinf and isnan) + +1.0.6 + * lth fix several compiler warnings + * lth fix generation of invalid json from yajl_gen_double + (NaN is not JSON) + * jstamp support for combining short options in tools + * jstamp exit properly on errors from tools + * octo test success no longer depends on integer size + * max fix configure --prefix + +1.0.5 + * lth several performance improvements related to function + inlinin' + +1.0.4 + * lth fix broken utf8 validation for three & four byte represenations. + thanks to http://github.com/brianmario and + http://github.com/technoweenie + +1.0.3 + * lth fix syntax error in cplusplus extern "C" statements for wider + compiler support + +1.0.2 + * lth update doxygen documentation with new sample code, passing NULL + for allocation functions added in 1.0.0 + +1.0.1 + * lth resolve crash in json_reformatter due to incorrectly ordered + parameters. + +1.0.0 + * lth add 'make install' rules, thaks to Andrei Soroker for the + contribution. + * lth client may override allocation routines at generator or parser + allocation time + * tjw add yajl_parse_complete routine to allow client to explicitly + specify end-of-input, solving the "lonely number" case, where + json text consists only of an element with no explicit syntactic + end. + * tjw many new test cases + * tjw cleanup of code for symmetry and ease of reading + * lth integration of patches from Robert Varga which cleanup + compilation warnings on 64 bit linux + +0.4.0 + * lth buffer overflow bug in yajl_gen_double s/%lf/%g/ - thanks to + Eric Bergstrome + * lth yajl_number callback to allow passthrough of arbitrary precision + numbers to client. Thanks to Hatem Nassrat. + * lth yajl_integer now deals in long, instead of long long. This + combined with yajl_number improves compiler compatibility while + maintaining precision. + * lth better ./configure && make experience (still requires cmake and + ruby) + * lth fix handling of special characters hex 0F and 1F in yajl_encode + (thanks to Robert Geiger) + * lth allow leading zeros in exponents (thanks to Hatem Nassrat) + +0.3.0 + * lth doxygen documentation (html & man) generated as part of the + build + * lth many documentation updates. + * lth fix to work with older versions of cmake (don't use LOOSE_LOOP + constructs) + * lth work around different behavior of freebsd 4 scanf. initialize + parameter to scanf to zero. + * lth all tests run 32x with ranging buffer sizes to stress stream + parsing + * lth yajl_test accepts -b option to allow read buffer size to be + set + * lth option to validate UTF8 added to parser (argument in + yajl_parser_cfg) + * lth fix buffer overrun when chunk ends inside \u escaped text + * lth support client cancelation + +0.2.2 + * lth on windows build debug with C7 symbols and no pdb files. + +0.2.1 + * fix yajl_reformat and yajl_verify to work on arbitrarily sized + inputs. + * fix win32 build break, clean up all errors and warnings. + * fix optimized build flags. + +0.2.0 + * optionally support comments in input text + +0.1.0 + * Initial release diff --git a/third-party/yajl/README b/third-party/yajl/README new file mode 100644 index 000000000..ad6175923 --- /dev/null +++ b/third-party/yajl/README @@ -0,0 +1,74 @@ +********************************************************************** + This is YAJL 2. For the legacy version of YAJL see + https://github.com/lloyd/yajl/tree/1.x +********************************************************************** + +Welcome to Yet Another JSON Library (YAJL) + +## Why does the world need another C library for parsing JSON? + +Good question. In a review of current C JSON parsing libraries I was +unable to find one that satisfies my requirements. Those are, +0. written in C +1. portable +2. robust -- as close to "crash proof" as possible +3. data representation independent +4. fast +5. generates verbose, useful error messages including context of where + the error occurs in the input text. +6. can parse JSON data off a stream, incrementally +7. simple to use +8. tiny + +Numbers 3, 5, 6, and 7 were particularly hard to find, and were what +caused me to ultimately create YAJL. This document is a tour of some +of the more important aspects of YAJL. + +## YAJL is Free. + +Permissive licensing means you can use it in open source and +commercial products alike without any fees. My request beyond the +licensing is that if you find bugs drop me a email, or better yet, +fork and fix. + +Porting YAJL should be trivial, the implementation is ANSI C. If you +port to new systems I'd love to hear of it and integrate your patches. + +## YAJL is data representation independent. + +BYODR! Many JSON libraries impose a structure based data representation +on you. This is a benefit in some cases and a drawback in others. +YAJL uses callbacks to remain agnostic of the in-memory representation. +So if you wish to build up an in-memory representation, you may do so +using YAJL, but you must bring the code that defines and populates the +in memory structure. + +This also means that YAJL can be used by other (higher level) JSON +libraries if so desired. + +## YAJL supports stream parsing + +This means you do not need to hold the whole JSON representation in +textual form in memory. This makes YAJL ideal for filtering projects, +where you're converting YAJL from one form to another (i.e. XML). The +included JSON pretty printer is an example of such a filter program. + +## YAJL is fast + +Minimal memory copying is performed. YAJL, when possible, returns +pointers into the client provided text (i.e. for strings that have no +embedded escape chars, hopefully the common case). I've put a lot of +effort into profiling and tuning performance, but I have ignored a +couple possible performance improvements to keep the interface clean, +small, and flexible. My hope is that YAJL will perform comparably to +the fastest JSON parser out there. + +YAJL should impose both minimal CPU and memory requirements on your +application. + +## YAJL is tiny. + +Fat free. No whip. + +enjoy, +Lloyd - July, 2007 diff --git a/third-party/yajl/TODO b/third-party/yajl/TODO new file mode 100644 index 000000000..56c3dc080 --- /dev/null +++ b/third-party/yajl/TODO @@ -0,0 +1,9 @@ +* add a test for 0x1F bug +* numeric overflow in integers and double +* line and char offsets in the lexer and in error messages +* testing: + a. the permuter + b. some performance comparison against json_checker. +* investigate pull instead of push parsing +* Handle memory allocation failures gracefully +* cygwin/msys support on win32 diff --git a/third-party/yajl/src/CMakeLists.txt b/third-party/yajl/src/CMakeLists.txt new file mode 100644 index 000000000..63a8ee904 --- /dev/null +++ b/third-party/yajl/src/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (c) 2007-2014, Lloyd Hilaiel +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +SET (SRCS yajl.c yajl_lex.c yajl_parser.c yajl_buf.c + yajl_encode.c yajl_gen.c yajl_alloc.c + yajl_tree.c yajl_version.c +) +SET (HDRS yajl_parser.h yajl_lex.h yajl_buf.h yajl_encode.h yajl_alloc.h) +SET (PUB_HDRS api/yajl_parse.h api/yajl_gen.h api/yajl_common.h api/yajl_tree.h) + +# useful when fixing lexer bugs. +#ADD_DEFINITIONS(-DYAJL_LEXER_DEBUG) + +# Ensure defined when building YAJL (as opposed to using it from +# another project). Used to ensure correct function export when +# building win32 DLL. +ADD_DEFINITIONS(-DYAJL_BUILD) + +# set up some paths +SET (incDir ${CMAKE_CURRENT_BINARY_DIR}/../${YAJL_DIST_NAME}/include/yajl) + +ADD_LIBRARY(yajl SHARED ${SRCS} ${HDRS} ${PUB_HDRS}) + +#### setup shared library version number +SET_TARGET_PROPERTIES(yajl PROPERTIES + INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2 + DEFINE_SYMBOL YAJL_SHARED + SOVERSION ${YAJL_MAJOR} + VERSION ${YAJL_MAJOR}.${YAJL_MINOR}.${YAJL_MICRO} + FOLDER Lib) + +#### build up an sdk as a post build step + +# create some directories +FILE(MAKE_DIRECTORY ${incDir}) + +# generate build-time source +CONFIGURE_FILE(api/yajl_version.h.cmake ${incDir}/yajl_version.h) + +# copy public headers to output directory +FOREACH (header ${PUB_HDRS}) + SET (header "${CMAKE_CURRENT_SOURCE_DIR}/${header}") + + EXEC_PROGRAM(${CMAKE_COMMAND} ARGS -E copy_if_different \"${header}\" \"${incDir}\") +ENDFOREACH (header ${PUB_HDRS}) + +INCLUDE_DIRECTORIES(${incDir}/..) + +INSTALL(TARGETS yajl + RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2) diff --git a/third-party/yajl/src/api/yajl_common.h b/third-party/yajl/src/api/yajl_common.h new file mode 100644 index 000000000..9596ef98d --- /dev/null +++ b/third-party/yajl/src/api/yajl_common.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __YAJL_COMMON_H__ +#define __YAJL_COMMON_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define YAJL_MAX_DEPTH 128 + +/* msft dll export gunk. To build a DLL on windows, you + * must define WIN32, YAJL_SHARED, and YAJL_BUILD. To use a shared + * DLL, you must define YAJL_SHARED and WIN32 */ +#if (defined(_WIN32) || defined(WIN32)) && defined(YAJL_SHARED) +# ifdef YAJL_BUILD +# define YAJL_API __declspec(dllexport) +# else +# define YAJL_API __declspec(dllimport) +# endif +#else +# if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303 +# define YAJL_API __attribute__ ((visibility("default"))) +# else +# define YAJL_API +# endif +#endif + +/** pointer to a malloc function, supporting client overriding memory + * allocation routines */ +typedef void * (*yajl_malloc_func)(void *ctx, size_t sz); + +/** pointer to a free function, supporting client overriding memory + * allocation routines */ +typedef void (*yajl_free_func)(void *ctx, void * ptr); + +/** pointer to a realloc function which can resize an allocation. */ +typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, size_t sz); + +/** A structure which can be passed to yajl_*_alloc routines to allow the + * client to specify memory allocation functions to be used. */ +typedef struct +{ + /** pointer to a function that can allocate uninitialized memory */ + yajl_malloc_func malloc; + /** pointer to a function that can resize memory allocations */ + yajl_realloc_func realloc; + /** pointer to a function that can free memory allocated using + * reallocFunction or mallocFunction */ + yajl_free_func free; + /** a context pointer that will be passed to above allocation routines */ + void * ctx; +} yajl_alloc_funcs; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third-party/yajl/src/api/yajl_gen.h b/third-party/yajl/src/api/yajl_gen.h new file mode 100644 index 000000000..ddd152729 --- /dev/null +++ b/third-party/yajl/src/api/yajl_gen.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file yajl_gen.h + * Interface to YAJL's JSON generation facilities. + */ + +#include + +#ifndef __YAJL_GEN_H__ +#define __YAJL_GEN_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + /** generator status codes */ + typedef enum { + /** no error */ + yajl_gen_status_ok = 0, + /** at a point where a map key is generated, a function other than + * yajl_gen_string was called */ + yajl_gen_keys_must_be_strings, + /** YAJL's maximum generation depth was exceeded. see + * YAJL_MAX_DEPTH */ + yajl_max_depth_exceeded, + /** A generator function (yajl_gen_XXX) was called while in an error + * state */ + yajl_gen_in_error_state, + /** A complete JSON document has been generated */ + yajl_gen_generation_complete, + /** yajl_gen_double was passed an invalid floating point value + * (infinity or NaN). */ + yajl_gen_invalid_number, + /** A print callback was passed in, so there is no internal + * buffer to get from */ + yajl_gen_no_buf, + /** returned from yajl_gen_string() when the yajl_gen_validate_utf8 + * option is enabled and an invalid was passed by client code. + */ + yajl_gen_invalid_string + } yajl_gen_status; + + /** an opaque handle to a generator */ + typedef struct yajl_gen_t * yajl_gen; + + /** a callback used for "printing" the results. */ + typedef void (*yajl_print_t)(void * ctx, + const char * str, + size_t len); + + /** configuration parameters for the parser, these may be passed to + * yajl_gen_config() along with option specific argument(s). In general, + * all configuration parameters default to *off*. */ + typedef enum { + /** generate indented (beautiful) output */ + yajl_gen_beautify = 0x01, + /** + * Set an indent string which is used when yajl_gen_beautify + * is enabled. Maybe something like \\t or some number of + * spaces. The default is four spaces ' '. + */ + yajl_gen_indent_string = 0x02, + /** + * Set a function and context argument that should be used to + * output generated json. the function should conform to the + * yajl_print_t prototype while the context argument is a + * void * of your choosing. + * + * example: + * yajl_gen_config(g, yajl_gen_print_callback, myFunc, myVoidPtr); + */ + yajl_gen_print_callback = 0x04, + /** + * Normally the generator does not validate that strings you + * pass to it via yajl_gen_string() are valid UTF8. Enabling + * this option will cause it to do so. + */ + yajl_gen_validate_utf8 = 0x08, + /** + * the forward solidus (slash or '/' in human) is not required to be + * escaped in json text. By default, YAJL will not escape it in the + * iterest of saving bytes. Setting this flag will cause YAJL to + * always escape '/' in generated JSON strings. + */ + yajl_gen_escape_solidus = 0x10 + } yajl_gen_option; + + /** allow the modification of generator options subsequent to handle + * allocation (via yajl_alloc) + * \returns zero in case of errors, non-zero otherwise + */ + YAJL_API int yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...); + + /** allocate a generator handle + * \param allocFuncs an optional pointer to a structure which allows + * the client to overide the memory allocation + * used by yajl. May be NULL, in which case + * malloc/free/realloc will be used. + * + * \returns an allocated handle on success, NULL on failure (bad params) + */ + YAJL_API yajl_gen yajl_gen_alloc(const yajl_alloc_funcs * allocFuncs); + + /** free a generator handle */ + YAJL_API void yajl_gen_free(yajl_gen handle); + + YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long long int number); + /** generate a floating point number. number may not be infinity or + * NaN, as these have no representation in JSON. In these cases the + * generator will return 'yajl_gen_invalid_number' */ + YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number); + YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand, + const char * num, + size_t len); + YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand, + const unsigned char * str, + size_t len); + YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean); + YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand); + + /** access the null terminated generator buffer. If incrementally + * outputing JSON, one should call yajl_gen_clear to clear the + * buffer. This allows stream generation. */ + YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand, + const unsigned char ** buf, + size_t * len); + + /** clear yajl's output buffer, but maintain all internal generation + * state. This function will not "reset" the generator state, and is + * intended to enable incremental JSON outputing. */ + YAJL_API void yajl_gen_clear(yajl_gen hand); + + /** Reset the generator state. Allows a client to generate multiple + * json entities in a stream. The "sep" string will be inserted to + * separate the previously generated entity from the current, + * NULL means *no separation* of entites (clients beware, generating + * multiple JSON numbers, for instance, will result in inscrutable + * output) */ + YAJL_API void yajl_gen_reset(yajl_gen hand, const char * sep); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third-party/yajl/src/api/yajl_parse.h b/third-party/yajl/src/api/yajl_parse.h new file mode 100644 index 000000000..1c25a60d5 --- /dev/null +++ b/third-party/yajl/src/api/yajl_parse.h @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file yajl_parse.h + * Interface to YAJL's JSON stream parsing facilities. + */ + +#include + +#ifndef __YAJL_PARSE_H__ +#define __YAJL_PARSE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + /** error codes returned from this interface */ + typedef enum { + /** no error was encountered */ + yajl_status_ok, + /** a client callback returned zero, stopping the parse */ + yajl_status_client_canceled, + /** An error occured during the parse. Call yajl_get_error for + * more information about the encountered error */ + yajl_status_error + } yajl_status; + + /** attain a human readable, english, string for an error */ + YAJL_API const char * yajl_status_to_string(yajl_status code); + + /** an opaque handle to a parser */ + typedef struct yajl_handle_t * yajl_handle; + + /** yajl is an event driven parser. this means as json elements are + * parsed, you are called back to do something with the data. The + * functions in this table indicate the various events for which + * you will be called back. Each callback accepts a "context" + * pointer, this is a void * that is passed into the yajl_parse + * function which the client code may use to pass around context. + * + * All callbacks return an integer. If non-zero, the parse will + * continue. If zero, the parse will be canceled and + * yajl_status_client_canceled will be returned from the parse. + * + * \attention { + * A note about the handling of numbers: + * + * yajl will only convert numbers that can be represented in a + * double or a 64 bit (long long) int. All other numbers will + * be passed to the client in string form using the yajl_number + * callback. Furthermore, if yajl_number is not NULL, it will + * always be used to return numbers, that is yajl_integer and + * yajl_double will be ignored. If yajl_number is NULL but one + * of yajl_integer or yajl_double are defined, parsing of a + * number larger than is representable in a double or 64 bit + * integer will result in a parse error. + * } + */ + typedef struct { + int (* yajl_null)(void * ctx); + int (* yajl_boolean)(void * ctx, int boolVal); + int (* yajl_integer)(void * ctx, long long integerVal); + int (* yajl_double)(void * ctx, double doubleVal); + /** A callback which passes the string representation of the number + * back to the client. Will be used for all numbers when present */ + int (* yajl_number)(void * ctx, const char * numberVal, + size_t numberLen); + + /** strings are returned as pointers into the JSON text when, + * possible, as a result, they are _not_ null padded */ + int (* yajl_string)(void * ctx, const unsigned char * stringVal, + size_t stringLen); + + int (* yajl_start_map)(void * ctx); + int (* yajl_map_key)(void * ctx, const unsigned char * key, + size_t stringLen); + int (* yajl_end_map)(void * ctx); + + int (* yajl_start_array)(void * ctx); + int (* yajl_end_array)(void * ctx); + } yajl_callbacks; + + /** allocate a parser handle + * \param callbacks a yajl callbacks structure specifying the + * functions to call when different JSON entities + * are encountered in the input text. May be NULL, + * which is only useful for validation. + * \param afs memory allocation functions, may be NULL for to use + * C runtime library routines (malloc and friends) + * \param ctx a context pointer that will be passed to callbacks. + */ + YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks, + yajl_alloc_funcs * afs, + void * ctx); + + + /** configuration parameters for the parser, these may be passed to + * yajl_config() along with option specific argument(s). In general, + * all configuration parameters default to *off*. */ + typedef enum { + /** Ignore javascript style comments present in + * JSON input. Non-standard, but rather fun + * arguments: toggled off with integer zero, on otherwise. + * + * example: + * yajl_config(h, yajl_allow_comments, 1); // turn comment support on + */ + yajl_allow_comments = 0x01, + /** + * When set the parser will verify that all strings in JSON input are + * valid UTF8 and will emit a parse error if this is not so. When set, + * this option makes parsing slightly more expensive (~7% depending + * on processor and compiler in use) + * + * example: + * yajl_config(h, yajl_dont_validate_strings, 1); // disable utf8 checking + */ + yajl_dont_validate_strings = 0x02, + /** + * By default, upon calls to yajl_complete_parse(), yajl will + * ensure the entire input text was consumed and will raise an error + * otherwise. Enabling this flag will cause yajl to disable this + * check. This can be useful when parsing json out of a that contains more + * than a single JSON document. + */ + yajl_allow_trailing_garbage = 0x04, + /** + * Allow multiple values to be parsed by a single handle. The + * entire text must be valid JSON, and values can be seperated + * by any kind of whitespace. This flag will change the + * behavior of the parser, and cause it continue parsing after + * a value is parsed, rather than transitioning into a + * complete state. This option can be useful when parsing multiple + * values from an input stream. + */ + yajl_allow_multiple_values = 0x08, + /** + * When yajl_complete_parse() is called the parser will + * check that the top level value was completely consumed. I.E., + * if called whilst in the middle of parsing a value + * yajl will enter an error state (premature EOF). Setting this + * flag suppresses that check and the corresponding error. + */ + yajl_allow_partial_values = 0x10 + } yajl_option; + + /** allow the modification of parser options subsequent to handle + * allocation (via yajl_alloc) + * \returns zero in case of errors, non-zero otherwise + */ + YAJL_API int yajl_config(yajl_handle h, yajl_option opt, ...); + + /** free a parser handle */ + YAJL_API void yajl_free(yajl_handle handle); + + /** Parse some json! + * \param hand - a handle to the json parser allocated with yajl_alloc + * \param jsonText - a pointer to the UTF8 json text to be parsed + * \param jsonTextLength - the length, in bytes, of input text + */ + YAJL_API yajl_status yajl_parse(yajl_handle hand, + const unsigned char * jsonText, + size_t jsonTextLength); + + /** Parse any remaining buffered json. + * Since yajl is a stream-based parser, without an explicit end of + * input, yajl sometimes can't decide if content at the end of the + * stream is valid or not. For example, if "1" has been fed in, + * yajl can't know whether another digit is next or some character + * that would terminate the integer token. + * + * \param hand - a handle to the json parser allocated with yajl_alloc + */ + YAJL_API yajl_status yajl_complete_parse(yajl_handle hand); + + /** get an error string describing the state of the + * parse. + * + * If verbose is non-zero, the message will include the JSON + * text where the error occured, along with an arrow pointing to + * the specific char. + * + * \returns A dynamically allocated string will be returned which should + * be freed with yajl_free_error + */ + YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose, + const unsigned char * jsonText, + size_t jsonTextLength); + + /** + * get the amount of data consumed from the last chunk passed to YAJL. + * + * In the case of a successful parse this can help you understand if + * the entire buffer was consumed (which will allow you to handle + * "junk at end of input"). + * + * In the event an error is encountered during parsing, this function + * affords the client a way to get the offset into the most recent + * chunk where the error occured. 0 will be returned if no error + * was encountered. + */ + YAJL_API size_t yajl_get_bytes_consumed(yajl_handle hand); + + /** free an error returned from yajl_get_error */ + YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third-party/yajl/src/api/yajl_tree.h b/third-party/yajl/src/api/yajl_tree.h new file mode 100644 index 000000000..1c1e06a58 --- /dev/null +++ b/third-party/yajl/src/api/yajl_tree.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2010-2011 Florian Forster + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file yajl_tree.h + * + * Parses JSON data and returns the data in tree form. + * + * \author Florian Forster + * \date August 2010 + * + * This interface makes quick parsing and extraction of + * smallish JSON docs trivial: + * + * \include example/parse_config.c + */ + +#ifndef YAJL_TREE_H +#define YAJL_TREE_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** possible data types that a yajl_val_s can hold */ +typedef enum { + yajl_t_string = 1, + yajl_t_number = 2, + yajl_t_object = 3, + yajl_t_array = 4, + yajl_t_true = 5, + yajl_t_false = 6, + yajl_t_null = 7, + /** The any type isn't valid for yajl_val_s.type, but can be + * used as an argument to routines like yajl_tree_get(). + */ + yajl_t_any = 8 +} yajl_type; + +#define YAJL_NUMBER_INT_VALID 0x01 +#define YAJL_NUMBER_DOUBLE_VALID 0x02 + +/** A pointer to a node in the parse tree */ +typedef struct yajl_val_s * yajl_val; + +/** + * A JSON value representation capable of holding one of the seven + * types above. For "string", "number", "object", and "array" + * additional data is available in the union. The "YAJL_IS_*" + * and "YAJL_GET_*" macros below allow type checking and convenient + * value extraction. + */ +struct yajl_val_s +{ + /** Type of the value contained. Use the "YAJL_IS_*" macros to check for a + * specific type. */ + yajl_type type; + /** Type-specific data. You may use the "YAJL_GET_*" macros to access these + * members. */ + union + { + char * string; + struct { + long long i; /*< integer value, if representable. */ + double d; /*< double value, if representable. */ + char *r; /*< unparsed number in string form. */ + /** Signals whether the \em i and \em d members are + * valid. See \c YAJL_NUMBER_INT_VALID and + * \c YAJL_NUMBER_DOUBLE_VALID. */ + unsigned int flags; + } number; + struct { + const char **keys; /*< Array of keys */ + yajl_val *values; /*< Array of values. */ + size_t len; /*< Number of key-value-pairs. */ + } object; + struct { + yajl_val *values; /*< Array of elements. */ + size_t len; /*< Number of elements. */ + } array; + } u; +}; + +/** + * Parse a string. + * + * Parses an null-terminated string containing JSON data and returns a pointer + * to the top-level value (root of the parse tree). + * + * \param input Pointer to a null-terminated utf8 string containing + * JSON data. + * \param error_buffer Pointer to a buffer in which an error message will + * be stored if \em yajl_tree_parse fails, or + * \c NULL. The buffer will be initialized before + * parsing, so its content will be destroyed even if + * \em yajl_tree_parse succeeds. + * \param error_buffer_size Size of the memory area pointed to by + * \em error_buffer_size. If \em error_buffer_size is + * \c NULL, this argument is ignored. + * + * \returns Pointer to the top-level value or \c NULL on error. The memory + * pointed to must be freed using \em yajl_tree_free. In case of an error, a + * null terminated message describing the error in more detail is stored in + * \em error_buffer if it is not \c NULL. + */ +YAJL_API yajl_val yajl_tree_parse (const char *input, + char *error_buffer, size_t error_buffer_size); + + +/** + * Free a parse tree returned by "yajl_tree_parse". + * + * \param v Pointer to a JSON value returned by "yajl_tree_parse". Passing NULL + * is valid and results in a no-op. + */ +YAJL_API void yajl_tree_free (yajl_val v); + +/** + * Access a nested value inside a tree. + * + * \param parent the node under which you'd like to extract values. + * \param path A null terminated array of strings, each the name of an object key + * \param type the yajl_type of the object you seek, or yajl_t_any if any will do. + * + * \returns a pointer to the found value, or NULL if we came up empty. + * + * Future Ideas: it'd be nice to move path to a string and implement support for + * a teeny tiny micro language here, so you can extract array elements, do things + * like .first and .last, even .length. Inspiration from JSONPath and css selectors? + * No it wouldn't be fast, but that's not what this API is about. + */ +YAJL_API yajl_val yajl_tree_get(yajl_val parent, const char ** path, yajl_type type); + +/* Various convenience macros to check the type of a `yajl_val` */ +#define YAJL_IS_STRING(v) (((v) != NULL) && ((v)->type == yajl_t_string)) +#define YAJL_IS_NUMBER(v) (((v) != NULL) && ((v)->type == yajl_t_number)) +#define YAJL_IS_INTEGER(v) (YAJL_IS_NUMBER(v) && ((v)->u.number.flags & YAJL_NUMBER_INT_VALID)) +#define YAJL_IS_DOUBLE(v) (YAJL_IS_NUMBER(v) && ((v)->u.number.flags & YAJL_NUMBER_DOUBLE_VALID)) +#define YAJL_IS_OBJECT(v) (((v) != NULL) && ((v)->type == yajl_t_object)) +#define YAJL_IS_ARRAY(v) (((v) != NULL) && ((v)->type == yajl_t_array )) +#define YAJL_IS_TRUE(v) (((v) != NULL) && ((v)->type == yajl_t_true )) +#define YAJL_IS_FALSE(v) (((v) != NULL) && ((v)->type == yajl_t_false )) +#define YAJL_IS_NULL(v) (((v) != NULL) && ((v)->type == yajl_t_null )) + +/** Given a yajl_val_string return a ptr to the bare string it contains, + * or NULL if the value is not a string. */ +#define YAJL_GET_STRING(v) (YAJL_IS_STRING(v) ? (v)->u.string : NULL) + +/** Get the string representation of a number. You should check type first, + * perhaps using YAJL_IS_NUMBER */ +#define YAJL_GET_NUMBER(v) ((v)->u.number.r) + +/** Get the double representation of a number. You should check type first, + * perhaps using YAJL_IS_DOUBLE */ +#define YAJL_GET_DOUBLE(v) ((v)->u.number.d) + +/** Get the 64bit (long long) integer representation of a number. You should + * check type first, perhaps using YAJL_IS_INTEGER */ +#define YAJL_GET_INTEGER(v) ((v)->u.number.i) + +/** Get a pointer to a yajl_val_object or NULL if the value is not an object. */ +#define YAJL_GET_OBJECT(v) (YAJL_IS_OBJECT(v) ? &(v)->u.object : NULL) + +/** Get a pointer to a yajl_val_array or NULL if the value is not an object. */ +#define YAJL_GET_ARRAY(v) (YAJL_IS_ARRAY(v) ? &(v)->u.array : NULL) + +#ifdef __cplusplus +} +#endif + +#endif /* YAJL_TREE_H */ diff --git a/third-party/yajl/src/api/yajl_version.h.cmake b/third-party/yajl/src/api/yajl_version.h.cmake new file mode 100644 index 000000000..4262ff71f --- /dev/null +++ b/third-party/yajl/src/api/yajl_version.h.cmake @@ -0,0 +1,23 @@ +#ifndef YAJL_VERSION_H_ +#define YAJL_VERSION_H_ + +#include + +#define YAJL_MAJOR ${YAJL_MAJOR} +#define YAJL_MINOR ${YAJL_MINOR} +#define YAJL_MICRO ${YAJL_MICRO} + +#define YAJL_VERSION ((YAJL_MAJOR * 10000) + (YAJL_MINOR * 100) + YAJL_MICRO) + +#ifdef __cplusplus +extern "C" { +#endif + +extern int YAJL_API yajl_version(void); + +#ifdef __cplusplus +} +#endif + +#endif /* YAJL_VERSION_H_ */ + diff --git a/third-party/yajl/src/yajl.c b/third-party/yajl/src/yajl.c new file mode 100644 index 000000000..d477893f1 --- /dev/null +++ b/third-party/yajl/src/yajl.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "api/yajl_parse.h" +#include "yajl_lex.h" +#include "yajl_parser.h" +#include "yajl_alloc.h" + +#include +#include +#include +#include + +const char * +yajl_status_to_string(yajl_status stat) +{ + const char * statStr = "unknown"; + switch (stat) { + case yajl_status_ok: + statStr = "ok, no error"; + break; + case yajl_status_client_canceled: + statStr = "client canceled parse"; + break; + case yajl_status_error: + statStr = "parse error"; + break; + } + return statStr; +} + +yajl_handle +yajl_alloc(const yajl_callbacks * callbacks, + yajl_alloc_funcs * afs, + void * ctx) +{ + yajl_handle hand = NULL; + yajl_alloc_funcs afsBuffer; + + /* first order of business is to set up memory allocation routines */ + if (afs != NULL) { + if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) + { + return NULL; + } + } else { + yajl_set_default_alloc_funcs(&afsBuffer); + afs = &afsBuffer; + } + + hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t)); + + /* copy in pointers to allocation routines */ + memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); + + hand->callbacks = callbacks; + hand->ctx = ctx; + hand->lexer = NULL; + hand->bytesConsumed = 0; + hand->decodeBuf = yajl_buf_alloc(&(hand->alloc)); + hand->flags = 0; + yajl_bs_init(hand->stateStack, &(hand->alloc)); + yajl_bs_push(hand->stateStack, yajl_state_start); + + return hand; +} + +int +yajl_config(yajl_handle h, yajl_option opt, ...) +{ + int rv = 1; + va_list ap; + va_start(ap, opt); + + switch(opt) { + case yajl_allow_comments: + case yajl_dont_validate_strings: + case yajl_allow_trailing_garbage: + case yajl_allow_multiple_values: + case yajl_allow_partial_values: + if (va_arg(ap, int)) h->flags |= opt; + else h->flags &= ~opt; + break; + default: + rv = 0; + } + va_end(ap); + + return rv; +} + +void +yajl_free(yajl_handle handle) +{ + yajl_bs_free(handle->stateStack); + yajl_buf_free(handle->decodeBuf); + if (handle->lexer) { + yajl_lex_free(handle->lexer); + handle->lexer = NULL; + } + YA_FREE(&(handle->alloc), handle); +} + +yajl_status +yajl_parse(yajl_handle hand, const unsigned char * jsonText, + size_t jsonTextLen) +{ + yajl_status status; + + /* lazy allocation of the lexer */ + if (hand->lexer == NULL) { + hand->lexer = yajl_lex_alloc(&(hand->alloc), + hand->flags & yajl_allow_comments, + !(hand->flags & yajl_dont_validate_strings)); + } + + status = yajl_do_parse(hand, jsonText, jsonTextLen); + return status; +} + + +yajl_status +yajl_complete_parse(yajl_handle hand) +{ + /* The lexer is lazy allocated in the first call to parse. if parse is + * never called, then no data was provided to parse at all. This is a + * "premature EOF" error unless yajl_allow_partial_values is specified. + * allocating the lexer now is the simplest possible way to handle this + * case while preserving all the other semantics of the parser + * (multiple values, partial values, etc). */ + if (hand->lexer == NULL) { + hand->lexer = yajl_lex_alloc(&(hand->alloc), + hand->flags & yajl_allow_comments, + !(hand->flags & yajl_dont_validate_strings)); + } + + return yajl_do_finish(hand); +} + +unsigned char * +yajl_get_error(yajl_handle hand, int verbose, + const unsigned char * jsonText, size_t jsonTextLen) +{ + return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose); +} + +size_t +yajl_get_bytes_consumed(yajl_handle hand) +{ + if (!hand) return 0; + else return hand->bytesConsumed; +} + + +void +yajl_free_error(yajl_handle hand, unsigned char * str) +{ + /* use memory allocation functions if set */ + YA_FREE(&(hand->alloc), str); +} + +/* XXX: add utility routines to parse from file */ diff --git a/third-party/yajl/src/yajl_alloc.c b/third-party/yajl/src/yajl_alloc.c new file mode 100644 index 000000000..96ad1d330 --- /dev/null +++ b/third-party/yajl/src/yajl_alloc.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file yajl_alloc.h + * default memory allocation routines for yajl which use malloc/realloc and + * free + */ + +#include "yajl_alloc.h" +#include + +static void * yajl_internal_malloc(void *ctx, size_t sz) +{ + (void)ctx; + return malloc(sz); +} + +static void * yajl_internal_realloc(void *ctx, void * previous, + size_t sz) +{ + (void)ctx; + return realloc(previous, sz); +} + +static void yajl_internal_free(void *ctx, void * ptr) +{ + (void)ctx; + free(ptr); +} + +void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf) +{ + yaf->malloc = yajl_internal_malloc; + yaf->free = yajl_internal_free; + yaf->realloc = yajl_internal_realloc; + yaf->ctx = NULL; +} + diff --git a/third-party/yajl/src/yajl_alloc.h b/third-party/yajl/src/yajl_alloc.h new file mode 100644 index 000000000..203c2f97b --- /dev/null +++ b/third-party/yajl/src/yajl_alloc.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file yajl_alloc.h + * default memory allocation routines for yajl which use malloc/realloc and + * free + */ + +#ifndef __YAJL_ALLOC_H__ +#define __YAJL_ALLOC_H__ + +#include "api/yajl_common.h" + +#define YA_MALLOC(afs, sz) (afs)->malloc((afs)->ctx, (sz)) +#define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr)) +#define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz)) + +void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf); + +#endif diff --git a/third-party/yajl/src/yajl_buf.c b/third-party/yajl/src/yajl_buf.c new file mode 100644 index 000000000..1aeafde08 --- /dev/null +++ b/third-party/yajl/src/yajl_buf.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "yajl_buf.h" + +#include +#include +#include + +#define YAJL_BUF_INIT_SIZE 2048 + +struct yajl_buf_t { + size_t len; + size_t used; + unsigned char * data; + yajl_alloc_funcs * alloc; +}; + +static +void yajl_buf_ensure_available(yajl_buf buf, size_t want) +{ + size_t need; + + assert(buf != NULL); + + /* first call */ + if (buf->data == NULL) { + buf->len = YAJL_BUF_INIT_SIZE; + buf->data = (unsigned char *) YA_MALLOC(buf->alloc, buf->len); + buf->data[0] = 0; + } + + need = buf->len; + + while (want >= (need - buf->used)) need <<= 1; + + if (need != buf->len) { + buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need); + buf->len = need; + } +} + +yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc) +{ + yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t)); + memset((void *) b, 0, sizeof(struct yajl_buf_t)); + b->alloc = alloc; + return b; +} + +void yajl_buf_free(yajl_buf buf) +{ + assert(buf != NULL); + if (buf->data) YA_FREE(buf->alloc, buf->data); + YA_FREE(buf->alloc, buf); +} + +void yajl_buf_append(yajl_buf buf, const void * data, size_t len) +{ + yajl_buf_ensure_available(buf, len); + if (len > 0) { + assert(data != NULL); + memcpy(buf->data + buf->used, data, len); + buf->used += len; + buf->data[buf->used] = 0; + } +} + +void yajl_buf_clear(yajl_buf buf) +{ + buf->used = 0; + if (buf->data) buf->data[buf->used] = 0; +} + +const unsigned char * yajl_buf_data(yajl_buf buf) +{ + return buf->data; +} + +size_t yajl_buf_len(yajl_buf buf) +{ + return buf->used; +} + +void +yajl_buf_truncate(yajl_buf buf, size_t len) +{ + assert(len <= buf->used); + buf->used = len; +} diff --git a/third-party/yajl/src/yajl_buf.h b/third-party/yajl/src/yajl_buf.h new file mode 100644 index 000000000..a35824630 --- /dev/null +++ b/third-party/yajl/src/yajl_buf.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __YAJL_BUF_H__ +#define __YAJL_BUF_H__ + +#include "api/yajl_common.h" +#include "yajl_alloc.h" + +/* + * Implementation/performance notes. If this were moved to a header + * only implementation using #define's where possible we might be + * able to sqeeze a little performance out of the guy by killing function + * call overhead. YMMV. + */ + +/** + * yajl_buf is a buffer with exponential growth. the buffer ensures that + * you are always null padded. + */ +typedef struct yajl_buf_t * yajl_buf; + +/* allocate a new buffer */ +yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc); + +/* free the buffer */ +void yajl_buf_free(yajl_buf buf); + +/* append a number of bytes to the buffer */ +void yajl_buf_append(yajl_buf buf, const void * data, size_t len); + +/* empty the buffer */ +void yajl_buf_clear(yajl_buf buf); + +/* get a pointer to the beginning of the buffer */ +const unsigned char * yajl_buf_data(yajl_buf buf); + +/* get the length of the buffer */ +size_t yajl_buf_len(yajl_buf buf); + +/* truncate the buffer */ +void yajl_buf_truncate(yajl_buf buf, size_t len); + +#endif diff --git a/third-party/yajl/src/yajl_bytestack.h b/third-party/yajl/src/yajl_bytestack.h new file mode 100644 index 000000000..9ea7d151e --- /dev/null +++ b/third-party/yajl/src/yajl_bytestack.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * A header only implementation of a simple stack of bytes, used in YAJL + * to maintain parse state. + */ + +#ifndef __YAJL_BYTESTACK_H__ +#define __YAJL_BYTESTACK_H__ + +#include "api/yajl_common.h" + +#define YAJL_BS_INC 128 + +typedef struct yajl_bytestack_t +{ + unsigned char * stack; + size_t size; + size_t used; + yajl_alloc_funcs * yaf; +} yajl_bytestack; + +/* initialize a bytestack */ +#define yajl_bs_init(obs, _yaf) { \ + (obs).stack = NULL; \ + (obs).size = 0; \ + (obs).used = 0; \ + (obs).yaf = (_yaf); \ + } \ + + +/* initialize a bytestack */ +#define yajl_bs_free(obs) \ + if ((obs).stack) (obs).yaf->free((obs).yaf->ctx, (obs).stack); + +#define yajl_bs_current(obs) \ + (assert((obs).used > 0), (obs).stack[(obs).used - 1]) + +#define yajl_bs_push(obs, byte) { \ + if (((obs).size - (obs).used) == 0) { \ + (obs).size += YAJL_BS_INC; \ + (obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\ + (void *) (obs).stack, (obs).size);\ + } \ + (obs).stack[((obs).used)++] = (byte); \ +} + +/* removes the top item of the stack, returns nothing */ +#define yajl_bs_pop(obs) { ((obs).used)--; } + +#define yajl_bs_set(obs, byte) \ + (obs).stack[((obs).used) - 1] = (byte); + + +#endif diff --git a/third-party/yajl/src/yajl_encode.c b/third-party/yajl/src/yajl_encode.c new file mode 100644 index 000000000..fd0825818 --- /dev/null +++ b/third-party/yajl/src/yajl_encode.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "yajl_encode.h" + +#include +#include +#include +#include + +static void CharToHex(unsigned char c, char * hexBuf) +{ + const char * hexchar = "0123456789ABCDEF"; + hexBuf[0] = hexchar[c >> 4]; + hexBuf[1] = hexchar[c & 0x0F]; +} + +void +yajl_string_encode(const yajl_print_t print, + void * ctx, + const unsigned char * str, + size_t len, + int escape_solidus) +{ + size_t beg = 0; + size_t end = 0; + char hexBuf[7]; + hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0'; + hexBuf[6] = 0; + + while (end < len) { + const char * escaped = NULL; + switch (str[end]) { + case '\r': escaped = "\\r"; break; + case '\n': escaped = "\\n"; break; + case '\\': escaped = "\\\\"; break; + /* it is not required to escape a solidus in JSON: + * read sec. 2.5: http://www.ietf.org/rfc/rfc4627.txt + * specifically, this production from the grammar: + * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + */ + case '/': if (escape_solidus) escaped = "\\/"; break; + case '"': escaped = "\\\""; break; + case '\f': escaped = "\\f"; break; + case '\b': escaped = "\\b"; break; + case '\t': escaped = "\\t"; break; + default: + if ((unsigned char) str[end] < 32) { + CharToHex(str[end], hexBuf + 4); + escaped = hexBuf; + } + break; + } + if (escaped != NULL) { + print(ctx, (const char *) (str + beg), end - beg); + print(ctx, escaped, (unsigned int)strlen(escaped)); + beg = ++end; + } else { + ++end; + } + } + print(ctx, (const char *) (str + beg), end - beg); +} + +static void hexToDigit(unsigned int * val, const unsigned char * hex) +{ + unsigned int i; + for (i=0;i<4;i++) { + unsigned char c = hex[i]; + if (c >= 'A') c = (c & ~0x20) - 7; + c -= '0'; + assert(!(c & 0xF0)); + *val = (*val << 4) | c; + } +} + +static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf) +{ + if (codepoint < 0x80) { + utf8Buf[0] = (char) codepoint; + utf8Buf[1] = 0; + } else if (codepoint < 0x0800) { + utf8Buf[0] = (char) ((codepoint >> 6) | 0xC0); + utf8Buf[1] = (char) ((codepoint & 0x3F) | 0x80); + utf8Buf[2] = 0; + } else if (codepoint < 0x10000) { + utf8Buf[0] = (char) ((codepoint >> 12) | 0xE0); + utf8Buf[1] = (char) (((codepoint >> 6) & 0x3F) | 0x80); + utf8Buf[2] = (char) ((codepoint & 0x3F) | 0x80); + utf8Buf[3] = 0; + } else if (codepoint < 0x200000) { + utf8Buf[0] =(char)((codepoint >> 18) | 0xF0); + utf8Buf[1] =(char)(((codepoint >> 12) & 0x3F) | 0x80); + utf8Buf[2] =(char)(((codepoint >> 6) & 0x3F) | 0x80); + utf8Buf[3] =(char)((codepoint & 0x3F) | 0x80); + utf8Buf[4] = 0; + } else { + utf8Buf[0] = '?'; + utf8Buf[1] = 0; + } +} + +void yajl_string_decode(yajl_buf buf, const unsigned char * str, + size_t len) +{ + size_t beg = 0; + size_t end = 0; + + while (end < len) { + if (str[end] == '\\') { + char utf8Buf[5]; + const char * unescaped = "?"; + yajl_buf_append(buf, str + beg, end - beg); + switch (str[++end]) { + case 'r': unescaped = "\r"; break; + case 'n': unescaped = "\n"; break; + case '\\': unescaped = "\\"; break; + case '/': unescaped = "/"; break; + case '"': unescaped = "\""; break; + case 'f': unescaped = "\f"; break; + case 'b': unescaped = "\b"; break; + case 't': unescaped = "\t"; break; + case 'u': { + unsigned int codepoint = 0; + hexToDigit(&codepoint, str + ++end); + end+=3; + /* check if this is a surrogate */ + if ((codepoint & 0xFC00) == 0xD800) { + end++; + if (str[end] == '\\' && str[end + 1] == 'u') { + unsigned int surrogate = 0; + hexToDigit(&surrogate, str + end + 2); + codepoint = + (((codepoint & 0x3F) << 10) | + ((((codepoint >> 6) & 0xF) + 1) << 16) | + (surrogate & 0x3FF)); + end += 5; + } else { + unescaped = "?"; + break; + } + } + + Utf32toUtf8(codepoint, utf8Buf); + unescaped = utf8Buf; + + if (codepoint == 0) { + yajl_buf_append(buf, unescaped, 1); + beg = ++end; + continue; + } + + break; + } + default: + assert("this should never happen" == NULL); + } + yajl_buf_append(buf, unescaped, (unsigned int)strlen(unescaped)); + beg = ++end; + } else { + end++; + } + } + yajl_buf_append(buf, str + beg, end - beg); +} + +#define ADV_PTR s++; if (!(len--)) return 0; + +int yajl_string_validate_utf8(const unsigned char * s, size_t len) +{ + if (!len) return 1; + if (!s) return 0; + + while (len--) { + /* single byte */ + if (*s <= 0x7f) { + /* noop */ + } + /* two byte */ + else if ((*s >> 5) == 0x6) { + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + } + /* three byte */ + else if ((*s >> 4) == 0x0e) { + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + } + /* four byte */ + else if ((*s >> 3) == 0x1e) { + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + } else { + return 0; + } + + s++; + } + + return 1; +} diff --git a/third-party/yajl/src/yajl_encode.h b/third-party/yajl/src/yajl_encode.h new file mode 100644 index 000000000..853a1a701 --- /dev/null +++ b/third-party/yajl/src/yajl_encode.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __YAJL_ENCODE_H__ +#define __YAJL_ENCODE_H__ + +#include "yajl_buf.h" +#include "api/yajl_gen.h" + +void yajl_string_encode(const yajl_print_t printer, + void * ctx, + const unsigned char * str, + size_t length, + int escape_solidus); + +void yajl_string_decode(yajl_buf buf, const unsigned char * str, + size_t length); + +int yajl_string_validate_utf8(const unsigned char * s, size_t len); + +#endif diff --git a/third-party/yajl/src/yajl_gen.c b/third-party/yajl/src/yajl_gen.c new file mode 100644 index 000000000..0f5c68e87 --- /dev/null +++ b/third-party/yajl/src/yajl_gen.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "api/yajl_gen.h" +#include "yajl_buf.h" +#include "yajl_encode.h" + +#include +#include +#include +#include +#include + +typedef enum { + yajl_gen_start, + yajl_gen_map_start, + yajl_gen_map_key, + yajl_gen_map_val, + yajl_gen_array_start, + yajl_gen_in_array, + yajl_gen_complete, + yajl_gen_error +} yajl_gen_state; + +struct yajl_gen_t +{ + unsigned int flags; + unsigned int depth; + const char * indentString; + yajl_gen_state state[YAJL_MAX_DEPTH]; + yajl_print_t print; + void * ctx; /* yajl_buf */ + /* memory allocation routines */ + yajl_alloc_funcs alloc; +}; + +int +yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...) +{ + int rv = 1; + va_list ap; + va_start(ap, opt); + + switch(opt) { + case yajl_gen_beautify: + case yajl_gen_validate_utf8: + case yajl_gen_escape_solidus: + if (va_arg(ap, int)) g->flags |= opt; + else g->flags &= ~opt; + break; + case yajl_gen_indent_string: { + const char *indent = va_arg(ap, const char *); + g->indentString = indent; + for (; *indent; indent++) { + if (*indent != '\n' + && *indent != '\v' + && *indent != '\f' + && *indent != '\t' + && *indent != '\r' + && *indent != ' ') + { + g->indentString = NULL; + rv = 0; + } + } + break; + } + case yajl_gen_print_callback: + yajl_buf_free(g->ctx); + g->print = va_arg(ap, const yajl_print_t); + g->ctx = va_arg(ap, void *); + break; + default: + rv = 0; + } + + va_end(ap); + + return rv; +} + + + +yajl_gen +yajl_gen_alloc(const yajl_alloc_funcs * afs) +{ + yajl_gen g = NULL; + yajl_alloc_funcs afsBuffer; + + /* first order of business is to set up memory allocation routines */ + if (afs != NULL) { + if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) + { + return NULL; + } + } else { + yajl_set_default_alloc_funcs(&afsBuffer); + afs = &afsBuffer; + } + + g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t)); + if (!g) return NULL; + + memset((void *) g, 0, sizeof(struct yajl_gen_t)); + /* copy in pointers to allocation routines */ + memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); + + g->print = (yajl_print_t)&yajl_buf_append; + g->ctx = yajl_buf_alloc(&(g->alloc)); + g->indentString = " "; + + return g; +} + +void +yajl_gen_reset(yajl_gen g, const char * sep) +{ + g->depth = 0; + memset((void *) &(g->state), 0, sizeof(g->state)); + if (sep != NULL) g->print(g->ctx, sep, strlen(sep)); +} + +void +yajl_gen_free(yajl_gen g) +{ + if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx); + YA_FREE(&(g->alloc), g); +} + +#define INSERT_SEP \ + if (g->state[g->depth] == yajl_gen_map_key || \ + g->state[g->depth] == yajl_gen_in_array) { \ + g->print(g->ctx, ",", 1); \ + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); \ + } else if (g->state[g->depth] == yajl_gen_map_val) { \ + g->print(g->ctx, ":", 1); \ + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1); \ + } + +#define INSERT_WHITESPACE \ + if ((g->flags & yajl_gen_beautify)) { \ + if (g->state[g->depth] != yajl_gen_map_val) { \ + unsigned int _i; \ + for (_i=0;_idepth;_i++) \ + g->print(g->ctx, \ + g->indentString, \ + (unsigned int)strlen(g->indentString)); \ + } \ + } + +#define ENSURE_NOT_KEY \ + if (g->state[g->depth] == yajl_gen_map_key || \ + g->state[g->depth] == yajl_gen_map_start) { \ + return yajl_gen_keys_must_be_strings; \ + } \ + +/* check that we're not complete, or in error state. in a valid state + * to be generating */ +#define ENSURE_VALID_STATE \ + if (g->state[g->depth] == yajl_gen_error) { \ + return yajl_gen_in_error_state;\ + } else if (g->state[g->depth] == yajl_gen_complete) { \ + return yajl_gen_generation_complete; \ + } + +#define INCREMENT_DEPTH \ + if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded; + +#define DECREMENT_DEPTH \ + if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_gen_generation_complete; + +#define APPENDED_ATOM \ + switch (g->state[g->depth]) { \ + case yajl_gen_start: \ + g->state[g->depth] = yajl_gen_complete; \ + break; \ + case yajl_gen_map_start: \ + case yajl_gen_map_key: \ + g->state[g->depth] = yajl_gen_map_val; \ + break; \ + case yajl_gen_array_start: \ + g->state[g->depth] = yajl_gen_in_array; \ + break; \ + case yajl_gen_map_val: \ + g->state[g->depth] = yajl_gen_map_key; \ + break; \ + default: \ + break; \ + } \ + +#define FINAL_NEWLINE \ + if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) \ + g->print(g->ctx, "\n", 1); + +yajl_gen_status +yajl_gen_integer(yajl_gen g, long long int number) +{ + char i[32]; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + sprintf(i, "%lld", number); + g->print(g->ctx, i, (unsigned int)strlen(i)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +#if defined(_WIN32) || defined(WIN32) +#include +#define isnan _isnan +#define isinf !_finite +#endif + +yajl_gen_status +yajl_gen_double(yajl_gen g, double number) +{ + char i[32]; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; + if (isnan(number) || isinf(number)) return yajl_gen_invalid_number; + INSERT_SEP; INSERT_WHITESPACE; + sprintf(i, "%.20g", number); + if (strspn(i, "0123456789-") == strlen(i)) { + strcat(i, ".0"); + } + g->print(g->ctx, i, (unsigned int)strlen(i)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_number(yajl_gen g, const char * s, size_t l) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, s, l); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_string(yajl_gen g, const unsigned char * str, + size_t len) +{ + // if validation is enabled, check that the string is valid utf8 + // XXX: This checking could be done a little faster, in the same pass as + // the string encoding + if (g->flags & yajl_gen_validate_utf8) { + if (!yajl_string_validate_utf8(str, len)) { + return yajl_gen_invalid_string; + } + } + ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, "\"", 1); + yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus); + g->print(g->ctx, "\"", 1); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_null(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, "null", strlen("null")); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_bool(yajl_gen g, int boolean) +{ + const char * val = boolean ? "true" : "false"; + + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, val, (unsigned int)strlen(val)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_map_open(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + INCREMENT_DEPTH; + + g->state[g->depth] = yajl_gen_map_start; + g->print(g->ctx, "{", 1); + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_map_close(yajl_gen g) +{ + ENSURE_VALID_STATE; + DECREMENT_DEPTH; + + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); + APPENDED_ATOM; + INSERT_WHITESPACE; + g->print(g->ctx, "}", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_array_open(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + INCREMENT_DEPTH; + g->state[g->depth] = yajl_gen_array_start; + g->print(g->ctx, "[", 1); + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_array_close(yajl_gen g) +{ + ENSURE_VALID_STATE; + DECREMENT_DEPTH; + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); + APPENDED_ATOM; + INSERT_WHITESPACE; + g->print(g->ctx, "]", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf, + size_t * len) +{ + if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf; + *buf = yajl_buf_data((yajl_buf)g->ctx); + *len = yajl_buf_len((yajl_buf)g->ctx); + return yajl_gen_status_ok; +} + +void +yajl_gen_clear(yajl_gen g) +{ + if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx); +} diff --git a/third-party/yajl/src/yajl_lex.c b/third-party/yajl/src/yajl_lex.c new file mode 100644 index 000000000..0b6f7ccfa --- /dev/null +++ b/third-party/yajl/src/yajl_lex.c @@ -0,0 +1,763 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "yajl_lex.h" +#include "yajl_buf.h" + +#include +#include +#include +#include + +#ifdef YAJL_LEXER_DEBUG +static const char * +tokToStr(yajl_tok tok) +{ + switch (tok) { + case yajl_tok_bool: return "bool"; + case yajl_tok_colon: return "colon"; + case yajl_tok_comma: return "comma"; + case yajl_tok_eof: return "eof"; + case yajl_tok_error: return "error"; + case yajl_tok_left_brace: return "brace"; + case yajl_tok_left_bracket: return "bracket"; + case yajl_tok_null: return "null"; + case yajl_tok_integer: return "integer"; + case yajl_tok_double: return "double"; + case yajl_tok_right_brace: return "brace"; + case yajl_tok_right_bracket: return "bracket"; + case yajl_tok_string: return "string"; + case yajl_tok_string_with_escapes: return "string_with_escapes"; + } + return "unknown"; +} +#endif + +/* Impact of the stream parsing feature on the lexer: + * + * YAJL support stream parsing. That is, the ability to parse the first + * bits of a chunk of JSON before the last bits are available (still on + * the network or disk). This makes the lexer more complex. The + * responsibility of the lexer is to handle transparently the case where + * a chunk boundary falls in the middle of a token. This is + * accomplished is via a buffer and a character reading abstraction. + * + * Overview of implementation + * + * When we lex to end of input string before end of token is hit, we + * copy all of the input text composing the token into our lexBuf. + * + * Every time we read a character, we do so through the readChar function. + * readChar's responsibility is to handle pulling all chars from the buffer + * before pulling chars from input text + */ + +struct yajl_lexer_t { + /* the overal line and char offset into the data */ + size_t lineOff; + size_t charOff; + + /* error */ + yajl_lex_error error; + + /* a input buffer to handle the case where a token is spread over + * multiple chunks */ + yajl_buf buf; + + /* in the case where we have data in the lexBuf, bufOff holds + * the current offset into the lexBuf. */ + size_t bufOff; + + /* are we using the lex buf? */ + unsigned int bufInUse; + + /* shall we allow comments? */ + unsigned int allowComments; + + /* shall we validate utf8 inside strings? */ + unsigned int validateUTF8; + + yajl_alloc_funcs * alloc; +}; + +#define readChar(lxr, txt, off) \ + (((lxr)->bufInUse && yajl_buf_len((lxr)->buf) && lxr->bufOff < yajl_buf_len((lxr)->buf)) ? \ + (*((const unsigned char *) yajl_buf_data((lxr)->buf) + ((lxr)->bufOff)++)) : \ + ((txt)[(*(off))++])) + +#define unreadChar(lxr, off) ((*(off) > 0) ? (*(off))-- : ((lxr)->bufOff--)) + +yajl_lexer +yajl_lex_alloc(yajl_alloc_funcs * alloc, + unsigned int allowComments, unsigned int validateUTF8) +{ + yajl_lexer lxr = (yajl_lexer) YA_MALLOC(alloc, sizeof(struct yajl_lexer_t)); + memset((void *) lxr, 0, sizeof(struct yajl_lexer_t)); + lxr->buf = yajl_buf_alloc(alloc); + lxr->allowComments = allowComments; + lxr->validateUTF8 = validateUTF8; + lxr->alloc = alloc; + return lxr; +} + +void +yajl_lex_free(yajl_lexer lxr) +{ + yajl_buf_free(lxr->buf); + YA_FREE(lxr->alloc, lxr); + return; +} + +/* a lookup table which lets us quickly determine three things: + * VEC - valid escaped control char + * note. the solidus '/' may be escaped or not. + * IJC - invalid json char + * VHC - valid hex char + * NFP - needs further processing (from a string scanning perspective) + * NUC - needs utf8 checking when enabled (from a string scanning perspective) + */ +#define VEC 0x01 +#define IJC 0x02 +#define VHC 0x04 +#define NFP 0x08 +#define NUC 0x10 + +static const char charLookupTable[256] = +{ +/*00*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*08*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*10*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*18*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , + +/*20*/ 0 , 0 , NFP|VEC|IJC, 0 , 0 , 0 , 0 , 0 , +/*28*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , VEC , +/*30*/ VHC , VHC , VHC , VHC , VHC , VHC , VHC , VHC , +/*38*/ VHC , VHC , 0 , 0 , 0 , 0 , 0 , 0 , + +/*40*/ 0 , VHC , VHC , VHC , VHC , VHC , VHC , 0 , +/*48*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +/*50*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +/*58*/ 0 , 0 , 0 , 0 , NFP|VEC|IJC, 0 , 0 , 0 , + +/*60*/ 0 , VHC , VEC|VHC, VHC , VHC , VHC , VEC|VHC, 0 , +/*68*/ 0 , 0 , 0 , 0 , 0 , 0 , VEC , 0 , +/*70*/ 0 , 0 , VEC , 0 , VEC , 0 , 0 , 0 , +/*78*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC +}; + +/** process a variable length utf8 encoded codepoint. + * + * returns: + * yajl_tok_string - if valid utf8 char was parsed and offset was + * advanced + * yajl_tok_eof - if end of input was hit before validation could + * complete + * yajl_tok_error - if invalid utf8 was encountered + * + * NOTE: on error the offset will point to the first char of the + * invalid utf8 */ +#define UTF8_CHECK_EOF if (*offset >= jsonTextLen) { return yajl_tok_eof; } + +static yajl_tok +yajl_lex_utf8_char(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset, + unsigned char curChar) +{ + if (curChar <= 0x7f) { + /* single byte */ + return yajl_tok_string; + } else if ((curChar >> 5) == 0x6) { + /* two byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } else if ((curChar >> 4) == 0x0e) { + /* three byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } + } else if ((curChar >> 3) == 0x1e) { + /* four byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } + } + } + + return yajl_tok_error; +} + +/* lex a string. input is the lexer, pointer to beginning of + * json text, and start of string (offset). + * a token is returned which has the following meanings: + * yajl_tok_string: lex of string was successful. offset points to + * terminating '"'. + * yajl_tok_eof: end of text was encountered before we could complete + * the lex. + * yajl_tok_error: embedded in the string were unallowable chars. offset + * points to the offending char + */ +#define STR_CHECK_EOF \ +if (*offset >= jsonTextLen) { \ + tok = yajl_tok_eof; \ + goto finish_string_lex; \ +} + +/** scan a string for interesting characters that might need further + * review. return the number of chars that are uninteresting and can + * be skipped. + * (lth) hi world, any thoughts on how to make this routine faster? */ +static size_t +yajl_string_scan(const unsigned char * buf, size_t len, int utf8check) +{ + unsigned char mask = IJC|NFP|(utf8check ? NUC : 0); + size_t skip = 0; + while (skip < len && !(charLookupTable[*buf] & mask)) + { + skip++; + buf++; + } + return skip; +} + +static yajl_tok +yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset) +{ + yajl_tok tok = yajl_tok_error; + int hasEscapes = 0; + + for (;;) { + unsigned char curChar; + + /* now jump into a faster scanning routine to skip as much + * of the buffers as possible */ + { + const unsigned char * p; + size_t len; + + if ((lexer->bufInUse && yajl_buf_len(lexer->buf) && + lexer->bufOff < yajl_buf_len(lexer->buf))) + { + p = ((const unsigned char *) yajl_buf_data(lexer->buf) + + (lexer->bufOff)); + len = yajl_buf_len(lexer->buf) - lexer->bufOff; + lexer->bufOff += yajl_string_scan(p, len, lexer->validateUTF8); + } + else if (*offset < jsonTextLen) + { + p = jsonText + *offset; + len = jsonTextLen - *offset; + *offset += yajl_string_scan(p, len, lexer->validateUTF8); + } + } + + STR_CHECK_EOF; + + curChar = readChar(lexer, jsonText, offset); + + /* quote terminates */ + if (curChar == '"') { + tok = yajl_tok_string; + break; + } + /* backslash escapes a set of control chars, */ + else if (curChar == '\\') { + hasEscapes = 1; + STR_CHECK_EOF; + + /* special case \u */ + curChar = readChar(lexer, jsonText, offset); + if (curChar == 'u') { + unsigned int i = 0; + + for (i=0;i<4;i++) { + STR_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if (!(charLookupTable[curChar] & VHC)) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_hex_char; + goto finish_string_lex; + } + } + } else if (!(charLookupTable[curChar] & VEC)) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_escaped_char; + goto finish_string_lex; + } + } + /* when not validating UTF8 it's a simple table lookup to determine + * if the present character is invalid */ + else if(charLookupTable[curChar] & IJC) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_json_char; + goto finish_string_lex; + } + /* when in validate UTF8 mode we need to do some extra work */ + else if (lexer->validateUTF8) { + yajl_tok t = yajl_lex_utf8_char(lexer, jsonText, jsonTextLen, + offset, curChar); + + if (t == yajl_tok_eof) { + tok = yajl_tok_eof; + goto finish_string_lex; + } else if (t == yajl_tok_error) { + lexer->error = yajl_lex_string_invalid_utf8; + goto finish_string_lex; + } + } + /* accept it, and move on */ + } + finish_string_lex: + /* tell our buddy, the parser, wether he needs to process this string + * again */ + if (hasEscapes && tok == yajl_tok_string) { + tok = yajl_tok_string_with_escapes; + } + + return tok; +} + +#define RETURN_IF_EOF if (*offset >= jsonTextLen) return yajl_tok_eof; + +static yajl_tok +yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset) +{ + /** XXX: numbers are the only entities in json that we must lex + * _beyond_ in order to know that they are complete. There + * is an ambiguous case for integers at EOF. */ + + unsigned char c; + + yajl_tok tok = yajl_tok_integer; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* optional leading minus */ + if (c == '-') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + /* a single zero, or a series of integers */ + if (c == '0') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } else if (c >= '1' && c <= '9') { + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c >= '0' && c <= '9'); + } else { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_minus; + return yajl_tok_error; + } + + /* optional fraction (indicates this is floating point) */ + if (c == '.') { + int numRd = 0; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + while (c >= '0' && c <= '9') { + numRd++; + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + if (!numRd) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_decimal; + return yajl_tok_error; + } + tok = yajl_tok_double; + } + + /* optional exponent (indicates this is floating point) */ + if (c == 'e' || c == 'E') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* optional sign */ + if (c == '+' || c == '-') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + if (c >= '0' && c <= '9') { + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c >= '0' && c <= '9'); + } else { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_exponent; + return yajl_tok_error; + } + tok = yajl_tok_double; + } + + /* we always go "one too far" */ + unreadChar(lexer, offset); + + return tok; +} + +static yajl_tok +yajl_lex_comment(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset) +{ + unsigned char c; + + yajl_tok tok = yajl_tok_comment; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* either slash or star expected */ + if (c == '/') { + /* now we throw away until end of line */ + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c != '\n'); + } else if (c == '*') { + /* now we throw away until end of comment */ + for (;;) { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + if (c == '*') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + if (c == '/') { + break; + } else { + unreadChar(lexer, offset); + } + } + } + } else { + lexer->error = yajl_lex_invalid_char; + tok = yajl_tok_error; + } + + return tok; +} + +yajl_tok +yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset, + const unsigned char ** outBuf, size_t * outLen) +{ + yajl_tok tok = yajl_tok_error; + unsigned char c; + size_t startOffset = *offset; + + *outBuf = NULL; + *outLen = 0; + + for (;;) { + assert(*offset <= jsonTextLen); + + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + + c = readChar(lexer, jsonText, offset); + + switch (c) { + case '{': + tok = yajl_tok_left_bracket; + goto lexed; + case '}': + tok = yajl_tok_right_bracket; + goto lexed; + case '[': + tok = yajl_tok_left_brace; + goto lexed; + case ']': + tok = yajl_tok_right_brace; + goto lexed; + case ',': + tok = yajl_tok_comma; + goto lexed; + case ':': + tok = yajl_tok_colon; + goto lexed; + case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': + startOffset++; + break; + case 't': { + const char * want = "rue"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_bool; + goto lexed; + } + case 'f': { + const char * want = "alse"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_bool; + goto lexed; + } + case 'n': { + const char * want = "ull"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_null; + goto lexed; + } + case '"': { + tok = yajl_lex_string(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + goto lexed; + } + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + /* integer parsing wants to start from the beginning */ + unreadChar(lexer, offset); + tok = yajl_lex_number(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + goto lexed; + } + case '/': + /* hey, look, a probable comment! If comments are disabled + * it's an error. */ + if (!lexer->allowComments) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_unallowed_comment; + tok = yajl_tok_error; + goto lexed; + } + /* if comments are enabled, then we should try to lex + * the thing. possible outcomes are + * - successful lex (tok_comment, which means continue), + * - malformed comment opening (slash not followed by + * '*' or '/') (tok_error) + * - eof hit. (tok_eof) */ + tok = yajl_lex_comment(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + if (tok == yajl_tok_comment) { + /* "error" is silly, but that's the initial + * state of tok. guilty until proven innocent. */ + tok = yajl_tok_error; + yajl_buf_clear(lexer->buf); + lexer->bufInUse = 0; + startOffset = *offset; + break; + } + /* hit error or eof, bail */ + goto lexed; + default: + lexer->error = yajl_lex_invalid_char; + tok = yajl_tok_error; + goto lexed; + } + } + + + lexed: + /* need to append to buffer if the buffer is in use or + * if it's an EOF token */ + if (tok == yajl_tok_eof || lexer->bufInUse) { + if (!lexer->bufInUse) yajl_buf_clear(lexer->buf); + lexer->bufInUse = 1; + yajl_buf_append(lexer->buf, jsonText + startOffset, *offset - startOffset); + lexer->bufOff = 0; + + if (tok != yajl_tok_eof) { + *outBuf = yajl_buf_data(lexer->buf); + *outLen = yajl_buf_len(lexer->buf); + lexer->bufInUse = 0; + } + } else if (tok != yajl_tok_error) { + *outBuf = jsonText + startOffset; + *outLen = *offset - startOffset; + } + + /* special case for strings. skip the quotes. */ + if (tok == yajl_tok_string || tok == yajl_tok_string_with_escapes) + { + assert(*outLen >= 2); + (*outBuf)++; + *outLen -= 2; + } + + +#ifdef YAJL_LEXER_DEBUG + if (tok == yajl_tok_error) { + printf("lexical error: %s\n", + yajl_lex_error_to_string(yajl_lex_get_error(lexer))); + } else if (tok == yajl_tok_eof) { + printf("EOF hit\n"); + } else { + printf("lexed %s: '", tokToStr(tok)); + fwrite(*outBuf, 1, *outLen, stdout); + printf("'\n"); + } +#endif + + return tok; +} + +const char * +yajl_lex_error_to_string(yajl_lex_error error) +{ + switch (error) { + case yajl_lex_e_ok: + return "ok, no error"; + case yajl_lex_string_invalid_utf8: + return "invalid bytes in UTF8 string."; + case yajl_lex_string_invalid_escaped_char: + return "inside a string, '\\' occurs before a character " + "which it may not."; + case yajl_lex_string_invalid_json_char: + return "invalid character inside string."; + case yajl_lex_string_invalid_hex_char: + return "invalid (non-hex) character occurs after '\\u' inside " + "string."; + case yajl_lex_invalid_char: + return "invalid char in json text."; + case yajl_lex_invalid_string: + return "invalid string in json text."; + case yajl_lex_missing_integer_after_exponent: + return "malformed number, a digit is required after the exponent."; + case yajl_lex_missing_integer_after_decimal: + return "malformed number, a digit is required after the " + "decimal point."; + case yajl_lex_missing_integer_after_minus: + return "malformed number, a digit is required after the " + "minus sign."; + case yajl_lex_unallowed_comment: + return "probable comment found in input text, comments are " + "not enabled."; + } + return "unknown error code"; +} + + +/** allows access to more specific information about the lexical + * error when yajl_lex_lex returns yajl_tok_error. */ +yajl_lex_error +yajl_lex_get_error(yajl_lexer lexer) +{ + if (lexer == NULL) return (yajl_lex_error) -1; + return lexer->error; +} + +size_t yajl_lex_current_line(yajl_lexer lexer) +{ + return lexer->lineOff; +} + +size_t yajl_lex_current_char(yajl_lexer lexer) +{ + return lexer->charOff; +} + +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t offset) +{ + const unsigned char * outBuf; + size_t outLen; + size_t bufLen = yajl_buf_len(lexer->buf); + size_t bufOff = lexer->bufOff; + unsigned int bufInUse = lexer->bufInUse; + yajl_tok tok; + + tok = yajl_lex_lex(lexer, jsonText, jsonTextLen, &offset, + &outBuf, &outLen); + + lexer->bufOff = bufOff; + lexer->bufInUse = bufInUse; + yajl_buf_truncate(lexer->buf, bufLen); + + return tok; +} diff --git a/third-party/yajl/src/yajl_lex.h b/third-party/yajl/src/yajl_lex.h new file mode 100644 index 000000000..fd17c001d --- /dev/null +++ b/third-party/yajl/src/yajl_lex.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __YAJL_LEX_H__ +#define __YAJL_LEX_H__ + +#include "api/yajl_common.h" + +typedef enum { + yajl_tok_bool, + yajl_tok_colon, + yajl_tok_comma, + yajl_tok_eof, + yajl_tok_error, + yajl_tok_left_brace, + yajl_tok_left_bracket, + yajl_tok_null, + yajl_tok_right_brace, + yajl_tok_right_bracket, + + /* we differentiate between integers and doubles to allow the + * parser to interpret the number without re-scanning */ + yajl_tok_integer, + yajl_tok_double, + + /* we differentiate between strings which require further processing, + * and strings that do not */ + yajl_tok_string, + yajl_tok_string_with_escapes, + + /* comment tokens are not currently returned to the parser, ever */ + yajl_tok_comment +} yajl_tok; + +typedef struct yajl_lexer_t * yajl_lexer; + +yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc, + unsigned int allowComments, + unsigned int validateUTF8); + +void yajl_lex_free(yajl_lexer lexer); + +/** + * run/continue a lex. "offset" is an input/output parameter. + * It should be initialized to zero for a + * new chunk of target text, and upon subsetquent calls with the same + * target text should passed with the value of the previous invocation. + * + * the client may be interested in the value of offset when an error is + * returned from the lexer. This allows the client to render useful + * error messages. + * + * When you pass the next chunk of data, context should be reinitialized + * to zero. + * + * Finally, the output buffer is usually just a pointer into the jsonText, + * however in cases where the entity being lexed spans multiple chunks, + * the lexer will buffer the entity and the data returned will be + * a pointer into that buffer. + * + * This behavior is abstracted from client code except for the performance + * implications which require that the client choose a reasonable chunk + * size to get adequate performance. + */ +yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset, + const unsigned char ** outBuf, size_t * outLen); + +/** have a peek at the next token, but don't move the lexer forward */ +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t offset); + + +typedef enum { + yajl_lex_e_ok = 0, + yajl_lex_string_invalid_utf8, + yajl_lex_string_invalid_escaped_char, + yajl_lex_string_invalid_json_char, + yajl_lex_string_invalid_hex_char, + yajl_lex_invalid_char, + yajl_lex_invalid_string, + yajl_lex_missing_integer_after_decimal, + yajl_lex_missing_integer_after_exponent, + yajl_lex_missing_integer_after_minus, + yajl_lex_unallowed_comment +} yajl_lex_error; + +const char * yajl_lex_error_to_string(yajl_lex_error error); + +/** allows access to more specific information about the lexical + * error when yajl_lex_lex returns yajl_tok_error. */ +yajl_lex_error yajl_lex_get_error(yajl_lexer lexer); + +/** get the current offset into the most recently lexed json string. */ +size_t yajl_lex_current_offset(yajl_lexer lexer); + +/** get the number of lines lexed by this lexer instance */ +size_t yajl_lex_current_line(yajl_lexer lexer); + +/** get the number of chars lexed by this lexer instance since the last + * \n or \r */ +size_t yajl_lex_current_char(yajl_lexer lexer); + +#endif diff --git a/third-party/yajl/src/yajl_parser.c b/third-party/yajl/src/yajl_parser.c new file mode 100644 index 000000000..1a528a640 --- /dev/null +++ b/third-party/yajl/src/yajl_parser.c @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "api/yajl_parse.h" +#include "yajl_lex.h" +#include "yajl_parser.h" +#include "yajl_encode.h" +#include "yajl_bytestack.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_VALUE_TO_MULTIPLY ((LLONG_MAX / 10) + (LLONG_MAX % 10)) + + /* same semantics as strtol */ +long long +yajl_parse_integer(const unsigned char *number, unsigned int length) +{ + long long ret = 0; + long sign = 1; + const unsigned char *pos = number; + if (*pos == '-') { pos++; sign = -1; } + if (*pos == '+') { pos++; } + + while (pos < number + length) { + if ( ret > MAX_VALUE_TO_MULTIPLY ) { + errno = ERANGE; + return sign == 1 ? LLONG_MAX : LLONG_MIN; + } + ret *= 10; + if (LLONG_MAX - ret < (*pos - '0')) { + errno = ERANGE; + return sign == 1 ? LLONG_MAX : LLONG_MIN; + } + if (*pos < '0' || *pos > '9') { + errno = ERANGE; + return sign == 1 ? LLONG_MAX : LLONG_MIN; + } + ret += (*pos++ - '0'); + } + + return sign * ret; +} + +unsigned char * +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, + size_t jsonTextLen, int verbose) +{ + size_t offset = hand->bytesConsumed; + unsigned char * str; + const char * errorType = NULL; + const char * errorText = NULL; + char text[72]; + const char * arrow = " (right here) ------^\n"; + + if (yajl_bs_current(hand->stateStack) == yajl_state_parse_error) { + errorType = "parse"; + errorText = hand->parseError; + } else if (yajl_bs_current(hand->stateStack) == yajl_state_lexical_error) { + errorType = "lexical"; + errorText = yajl_lex_error_to_string(yajl_lex_get_error(hand->lexer)); + } else { + errorType = "unknown"; + } + + { + size_t memneeded = 0; + memneeded += strlen(errorType); + memneeded += strlen(" error"); + if (errorText != NULL) { + memneeded += strlen(": "); + memneeded += strlen(errorText); + } + str = (unsigned char *) YA_MALLOC(&(hand->alloc), memneeded + 2); + if (!str) return NULL; + str[0] = 0; + strcat((char *) str, errorType); + strcat((char *) str, " error"); + if (errorText != NULL) { + strcat((char *) str, ": "); + strcat((char *) str, errorText); + } + strcat((char *) str, "\n"); + } + + /* now we append as many spaces as needed to make sure the error + * falls at char 41, if verbose was specified */ + if (verbose) { + size_t start, end, i; + size_t spacesNeeded; + + spacesNeeded = (offset < 30 ? 40 - offset : 10); + start = (offset >= 30 ? offset - 30 : 0); + end = (offset + 30 > jsonTextLen ? jsonTextLen : offset + 30); + + for (i=0;ialloc), (unsigned int)(strlen((char *) str) + + strlen((char *) text) + + strlen(arrow) + 1)); + if (newStr) { + newStr[0] = 0; + strcat((char *) newStr, (char *) str); + strcat((char *) newStr, text); + strcat((char *) newStr, arrow); + } + YA_FREE(&(hand->alloc), str); + str = (unsigned char *) newStr; + } + } + return str; +} + +/* check for client cancelation */ +#define _CC_CHK(x) \ + if (!(x)) { \ + yajl_bs_set(hand->stateStack, yajl_state_parse_error); \ + hand->parseError = \ + "client cancelled parse via callback return value"; \ + return yajl_status_client_canceled; \ + } + + +yajl_status +yajl_do_finish(yajl_handle hand) +{ + yajl_status stat; + stat = yajl_do_parse(hand,(const unsigned char *) " ",1); + + if (stat != yajl_status_ok) return stat; + + switch(yajl_bs_current(hand->stateStack)) + { + case yajl_state_parse_error: + case yajl_state_lexical_error: + return yajl_status_error; + case yajl_state_got_value: + case yajl_state_parse_complete: + return yajl_status_ok; + default: + if (!(hand->flags & yajl_allow_partial_values)) + { + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "premature EOF"; + return yajl_status_error; + } + return yajl_status_ok; + } +} + +yajl_status +yajl_do_parse(yajl_handle hand, const unsigned char * jsonText, + size_t jsonTextLen) +{ + yajl_tok tok; + const unsigned char * buf; + size_t bufLen; + size_t * offset = &(hand->bytesConsumed); + + *offset = 0; + + around_again: + switch (yajl_bs_current(hand->stateStack)) { + case yajl_state_parse_complete: + if (hand->flags & yajl_allow_multiple_values) { + yajl_bs_set(hand->stateStack, yajl_state_got_value); + goto around_again; + } + if (!(hand->flags & yajl_allow_trailing_garbage)) { + if (*offset != jsonTextLen) { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + if (tok != yajl_tok_eof) { + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "trailing garbage"; + } + goto around_again; + } + } + return yajl_status_ok; + case yajl_state_lexical_error: + case yajl_state_parse_error: + return yajl_status_error; + case yajl_state_start: + case yajl_state_got_value: + case yajl_state_map_need_val: + case yajl_state_array_need_val: + case yajl_state_array_start: { + /* for arrays and maps, we advance the state for this + * depth, then push the state of the next depth. + * If an error occurs during the parsing of the nesting + * enitity, the state at this level will not matter. + * a state that needs pushing will be anything other + * than state_start */ + + yajl_state stateToPush = yajl_state_start; + + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + + switch (tok) { + case yajl_tok_eof: + return yajl_status_ok; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + case yajl_tok_string: + if (hand->callbacks && hand->callbacks->yajl_string) { + _CC_CHK(hand->callbacks->yajl_string(hand->ctx, + buf, bufLen)); + } + break; + case yajl_tok_string_with_escapes: + if (hand->callbacks && hand->callbacks->yajl_string) { + yajl_buf_clear(hand->decodeBuf); + yajl_string_decode(hand->decodeBuf, buf, bufLen); + _CC_CHK(hand->callbacks->yajl_string( + hand->ctx, yajl_buf_data(hand->decodeBuf), + yajl_buf_len(hand->decodeBuf))); + } + break; + case yajl_tok_bool: + if (hand->callbacks && hand->callbacks->yajl_boolean) { + _CC_CHK(hand->callbacks->yajl_boolean(hand->ctx, + *buf == 't')); + } + break; + case yajl_tok_null: + if (hand->callbacks && hand->callbacks->yajl_null) { + _CC_CHK(hand->callbacks->yajl_null(hand->ctx)); + } + break; + case yajl_tok_left_bracket: + if (hand->callbacks && hand->callbacks->yajl_start_map) { + _CC_CHK(hand->callbacks->yajl_start_map(hand->ctx)); + } + stateToPush = yajl_state_map_start; + break; + case yajl_tok_left_brace: + if (hand->callbacks && hand->callbacks->yajl_start_array) { + _CC_CHK(hand->callbacks->yajl_start_array(hand->ctx)); + } + stateToPush = yajl_state_array_start; + break; + case yajl_tok_integer: + if (hand->callbacks) { + if (hand->callbacks->yajl_number) { + _CC_CHK(hand->callbacks->yajl_number( + hand->ctx,(const char *) buf, bufLen)); + } else if (hand->callbacks->yajl_integer) { + long long int i = 0; + errno = 0; + i = yajl_parse_integer(buf, bufLen); + if ((i == LLONG_MIN || i == LLONG_MAX) && + errno == ERANGE) + { + yajl_bs_set(hand->stateStack, + yajl_state_parse_error); + hand->parseError = "integer overflow" ; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + _CC_CHK(hand->callbacks->yajl_integer(hand->ctx, + i)); + } + } + break; + case yajl_tok_double: + if (hand->callbacks) { + if (hand->callbacks->yajl_number) { + _CC_CHK(hand->callbacks->yajl_number( + hand->ctx, (const char *) buf, bufLen)); + } else if (hand->callbacks->yajl_double) { + double d = 0.0; + yajl_buf_clear(hand->decodeBuf); + yajl_buf_append(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + errno = 0; + d = strtod((char *) buf, NULL); + if ((d == HUGE_VAL || d == -HUGE_VAL) && + errno == ERANGE) + { + yajl_bs_set(hand->stateStack, + yajl_state_parse_error); + hand->parseError = "numeric (floating point) " + "overflow"; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + _CC_CHK(hand->callbacks->yajl_double(hand->ctx, + d)); + } + } + break; + case yajl_tok_right_brace: { + if (yajl_bs_current(hand->stateStack) == + yajl_state_array_start) + { + if (hand->callbacks && + hand->callbacks->yajl_end_array) + { + _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + } + /* intentional fall-through */ + } + case yajl_tok_colon: + case yajl_tok_comma: + case yajl_tok_right_bracket: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "unallowed token at this point in JSON text"; + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "invalid token, internal error"; + goto around_again; + } + /* got a value. transition depends on the state we're in. */ + { + yajl_state s = yajl_bs_current(hand->stateStack); + if (s == yajl_state_start || s == yajl_state_got_value) { + yajl_bs_set(hand->stateStack, yajl_state_parse_complete); + } else if (s == yajl_state_map_need_val) { + yajl_bs_set(hand->stateStack, yajl_state_map_got_val); + } else { + yajl_bs_set(hand->stateStack, yajl_state_array_got_val); + } + } + if (stateToPush != yajl_state_start) { + yajl_bs_push(hand->stateStack, stateToPush); + } + + goto around_again; + } + case yajl_state_map_start: + case yajl_state_map_need_key: { + /* only difference between these two states is that in + * start '}' is valid, whereas in need_key, we've parsed + * a comma, and a string key _must_ follow */ + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_eof: + return yajl_status_ok; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + case yajl_tok_string_with_escapes: + if (hand->callbacks && hand->callbacks->yajl_map_key) { + yajl_buf_clear(hand->decodeBuf); + yajl_string_decode(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + bufLen = yajl_buf_len(hand->decodeBuf); + } + /* intentional fall-through */ + case yajl_tok_string: + if (hand->callbacks && hand->callbacks->yajl_map_key) { + _CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf, + bufLen)); + } + yajl_bs_set(hand->stateStack, yajl_state_map_sep); + goto around_again; + case yajl_tok_right_bracket: + if (yajl_bs_current(hand->stateStack) == + yajl_state_map_start) + { + if (hand->callbacks && hand->callbacks->yajl_end_map) { + _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + } + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "invalid object key (must be a string)"; + goto around_again; + } + } + case yajl_state_map_sep: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_colon: + yajl_bs_set(hand->stateStack, yajl_state_map_need_val); + goto around_again; + case yajl_tok_eof: + return yajl_status_ok; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "object key and value must " + "be separated by a colon (':')"; + goto around_again; + } + } + case yajl_state_map_got_val: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_right_bracket: + if (hand->callbacks && hand->callbacks->yajl_end_map) { + _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + case yajl_tok_comma: + yajl_bs_set(hand->stateStack, yajl_state_map_need_key); + goto around_again; + case yajl_tok_eof: + return yajl_status_ok; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "after key and value, inside map, " + "I expect ',' or '}'"; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + } + case yajl_state_array_got_val: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_right_brace: + if (hand->callbacks && hand->callbacks->yajl_end_array) { + _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + case yajl_tok_comma: + yajl_bs_set(hand->stateStack, yajl_state_array_need_val); + goto around_again; + case yajl_tok_eof: + return yajl_status_ok; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "after array element, I expect ',' or ']'"; + goto around_again; + } + } + } + + abort(); + return yajl_status_error; +} + diff --git a/third-party/yajl/src/yajl_parser.h b/third-party/yajl/src/yajl_parser.h new file mode 100644 index 000000000..c79299a08 --- /dev/null +++ b/third-party/yajl/src/yajl_parser.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __YAJL_PARSER_H__ +#define __YAJL_PARSER_H__ + +#include "api/yajl_parse.h" +#include "yajl_bytestack.h" +#include "yajl_buf.h" +#include "yajl_lex.h" + + +typedef enum { + yajl_state_start = 0, + yajl_state_parse_complete, + yajl_state_parse_error, + yajl_state_lexical_error, + yajl_state_map_start, + yajl_state_map_sep, + yajl_state_map_need_val, + yajl_state_map_got_val, + yajl_state_map_need_key, + yajl_state_array_start, + yajl_state_array_got_val, + yajl_state_array_need_val, + yajl_state_got_value, +} yajl_state; + +struct yajl_handle_t { + const yajl_callbacks * callbacks; + void * ctx; + yajl_lexer lexer; + const char * parseError; + /* the number of bytes consumed from the last client buffer, + * in the case of an error this will be an error offset, in the + * case of an error this can be used as the error offset */ + size_t bytesConsumed; + /* temporary storage for decoded strings */ + yajl_buf decodeBuf; + /* a stack of states. access with yajl_state_XXX routines */ + yajl_bytestack stateStack; + /* memory allocation routines */ + yajl_alloc_funcs alloc; + /* bitfield */ + unsigned int flags; +}; + +yajl_status +yajl_do_parse(yajl_handle handle, const unsigned char * jsonText, + size_t jsonTextLen); + +yajl_status +yajl_do_finish(yajl_handle handle); + +unsigned char * +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, + size_t jsonTextLen, int verbose); + +/* A little built in integer parsing routine with the same semantics as strtol + * that's unaffected by LOCALE. */ +long long +yajl_parse_integer(const unsigned char *number, unsigned int length); + + +#endif diff --git a/third-party/yajl/src/yajl_tree.c b/third-party/yajl/src/yajl_tree.c new file mode 100644 index 000000000..3d357a32c --- /dev/null +++ b/third-party/yajl/src/yajl_tree.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2010-2011 Florian Forster + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "api/yajl_tree.h" +#include "api/yajl_parse.h" + +#include "yajl_parser.h" + +#if defined(_WIN32) || defined(WIN32) +#define snprintf sprintf_s +#endif + +#define STATUS_CONTINUE 1 +#define STATUS_ABORT 0 + +struct stack_elem_s; +typedef struct stack_elem_s stack_elem_t; +struct stack_elem_s +{ + char * key; + yajl_val value; + stack_elem_t *next; +}; + +struct context_s +{ + stack_elem_t *stack; + yajl_val root; + char *errbuf; + size_t errbuf_size; +}; +typedef struct context_s context_t; + +#define RETURN_ERROR(ctx,retval,...) { \ + if ((ctx)->errbuf != NULL) \ + snprintf ((ctx)->errbuf, (ctx)->errbuf_size, __VA_ARGS__); \ + return (retval); \ + } + +static yajl_val value_alloc (yajl_type type) +{ + yajl_val v; + + v = malloc (sizeof (*v)); + if (v == NULL) return (NULL); + memset (v, 0, sizeof (*v)); + v->type = type; + + return (v); +} + +static void yajl_object_free (yajl_val v) +{ + size_t i; + + if (!YAJL_IS_OBJECT(v)) return; + + for (i = 0; i < v->u.object.len; i++) + { + free((char *) v->u.object.keys[i]); + v->u.object.keys[i] = NULL; + yajl_tree_free (v->u.object.values[i]); + v->u.object.values[i] = NULL; + } + + free((void*) v->u.object.keys); + free(v->u.object.values); + free(v); +} + +static void yajl_array_free (yajl_val v) +{ + size_t i; + + if (!YAJL_IS_ARRAY(v)) return; + + for (i = 0; i < v->u.array.len; i++) + { + yajl_tree_free (v->u.array.values[i]); + v->u.array.values[i] = NULL; + } + + free(v->u.array.values); + free(v); +} + +/* + * Parsing nested objects and arrays is implemented using a stack. When a new + * object or array starts (a curly or a square opening bracket is read), an + * appropriate value is pushed on the stack. When the end of the object is + * reached (an appropriate closing bracket has been read), the value is popped + * off the stack and added to the enclosing object using "context_add_value". + */ +static int context_push(context_t *ctx, yajl_val v) +{ + stack_elem_t *stack; + + stack = malloc (sizeof (*stack)); + if (stack == NULL) + RETURN_ERROR (ctx, ENOMEM, "Out of memory"); + memset (stack, 0, sizeof (*stack)); + + assert ((ctx->stack == NULL) + || YAJL_IS_OBJECT (v) + || YAJL_IS_ARRAY (v)); + + stack->value = v; + stack->next = ctx->stack; + ctx->stack = stack; + + return (0); +} + +static yajl_val context_pop(context_t *ctx) +{ + stack_elem_t *stack; + yajl_val v; + + if (ctx->stack == NULL) + RETURN_ERROR (ctx, NULL, "context_pop: " + "Bottom of stack reached prematurely"); + + stack = ctx->stack; + ctx->stack = stack->next; + + v = stack->value; + + free (stack); + + return (v); +} + +static int object_add_keyval(context_t *ctx, + yajl_val obj, char *key, yajl_val value) +{ + const char **tmpk; + yajl_val *tmpv; + + /* We're checking for NULL in "context_add_value" or its callers. */ + assert (ctx != NULL); + assert (obj != NULL); + assert (key != NULL); + assert (value != NULL); + + /* We're assuring that "obj" is an object in "context_add_value". */ + assert(YAJL_IS_OBJECT(obj)); + + tmpk = realloc((void *) obj->u.object.keys, sizeof(*(obj->u.object.keys)) * (obj->u.object.len + 1)); + if (tmpk == NULL) + RETURN_ERROR(ctx, ENOMEM, "Out of memory"); + obj->u.object.keys = tmpk; + + tmpv = realloc(obj->u.object.values, sizeof (*obj->u.object.values) * (obj->u.object.len + 1)); + if (tmpv == NULL) + RETURN_ERROR(ctx, ENOMEM, "Out of memory"); + obj->u.object.values = tmpv; + + obj->u.object.keys[obj->u.object.len] = key; + obj->u.object.values[obj->u.object.len] = value; + obj->u.object.len++; + + return (0); +} + +static int array_add_value (context_t *ctx, + yajl_val array, yajl_val value) +{ + yajl_val *tmp; + + /* We're checking for NULL pointers in "context_add_value" or its + * callers. */ + assert (ctx != NULL); + assert (array != NULL); + assert (value != NULL); + + /* "context_add_value" will only call us with array values. */ + assert(YAJL_IS_ARRAY(array)); + + tmp = realloc(array->u.array.values, + sizeof(*(array->u.array.values)) * (array->u.array.len + 1)); + if (tmp == NULL) + RETURN_ERROR(ctx, ENOMEM, "Out of memory"); + array->u.array.values = tmp; + array->u.array.values[array->u.array.len] = value; + array->u.array.len++; + + return 0; +} + +/* + * Add a value to the value on top of the stack or the "root" member in the + * context if the end of the parsing process is reached. + */ +static int context_add_value (context_t *ctx, yajl_val v) +{ + /* We're checking for NULL values in all the calling functions. */ + assert (ctx != NULL); + assert (v != NULL); + + /* + * There are three valid states in which this function may be called: + * - There is no value on the stack => This is the only value. This is the + * last step done when parsing a document. We assign the value to the + * "root" member and return. + * - The value on the stack is an object. In this case store the key on the + * stack or, if the key has already been read, add key and value to the + * object. + * - The value on the stack is an array. In this case simply add the value + * and return. + */ + if (ctx->stack == NULL) + { + assert (ctx->root == NULL); + ctx->root = v; + return (0); + } + else if (YAJL_IS_OBJECT (ctx->stack->value)) + { + if (ctx->stack->key == NULL) + { + if (!YAJL_IS_STRING (v)) + RETURN_ERROR (ctx, EINVAL, "context_add_value: " + "Object key is not a string (%#04x)", + v->type); + + ctx->stack->key = v->u.string; + v->u.string = NULL; + free(v); + return (0); + } + else /* if (ctx->key != NULL) */ + { + char * key; + + key = ctx->stack->key; + ctx->stack->key = NULL; + return (object_add_keyval (ctx, ctx->stack->value, key, v)); + } + } + else if (YAJL_IS_ARRAY (ctx->stack->value)) + { + return (array_add_value (ctx, ctx->stack->value, v)); + } + else + { + RETURN_ERROR (ctx, EINVAL, "context_add_value: Cannot add value to " + "a value of type %#04x (not a composite type)", + ctx->stack->value->type); + } +} + +static int handle_string (void *ctx, + const unsigned char *string, size_t string_length) +{ + yajl_val v; + + v = value_alloc (yajl_t_string); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + + v->u.string = malloc (string_length + 1); + if (v->u.string == NULL) + { + free (v); + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + } + memcpy(v->u.string, string, string_length); + v->u.string[string_length] = 0; + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} + +static int handle_number (void *ctx, const char *string, size_t string_length) +{ + yajl_val v; + char *endptr; + + v = value_alloc(yajl_t_number); + if (v == NULL) + RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory"); + + v->u.number.r = malloc(string_length + 1); + if (v->u.number.r == NULL) + { + free(v); + RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory"); + } + memcpy(v->u.number.r, string, string_length); + v->u.number.r[string_length] = 0; + + v->u.number.flags = 0; + + errno = 0; + v->u.number.i = yajl_parse_integer((const unsigned char *) v->u.number.r, + strlen(v->u.number.r)); + if (errno == 0) + v->u.number.flags |= YAJL_NUMBER_INT_VALID; + + endptr = NULL; + errno = 0; + v->u.number.d = strtod(v->u.number.r, &endptr); + if ((errno == 0) && (endptr != NULL) && (*endptr == 0)) + v->u.number.flags |= YAJL_NUMBER_DOUBLE_VALID; + + return ((context_add_value(ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} + +static int handle_start_map (void *ctx) +{ + yajl_val v; + + v = value_alloc(yajl_t_object); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + + v->u.object.keys = NULL; + v->u.object.values = NULL; + v->u.object.len = 0; + + return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} + +static int handle_end_map (void *ctx) +{ + yajl_val v; + + v = context_pop (ctx); + if (v == NULL) + return (STATUS_ABORT); + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} + +static int handle_start_array (void *ctx) +{ + yajl_val v; + + v = value_alloc(yajl_t_array); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + + v->u.array.values = NULL; + v->u.array.len = 0; + + return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} + +static int handle_end_array (void *ctx) +{ + yajl_val v; + + v = context_pop (ctx); + if (v == NULL) + return (STATUS_ABORT); + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} + +static int handle_boolean (void *ctx, int boolean_value) +{ + yajl_val v; + + v = value_alloc (boolean_value ? yajl_t_true : yajl_t_false); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} + +static int handle_null (void *ctx) +{ + yajl_val v; + + v = value_alloc (yajl_t_null); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} + +/* + * Public functions + */ +yajl_val yajl_tree_parse (const char *input, + char *error_buffer, size_t error_buffer_size) +{ + static const yajl_callbacks callbacks = + { + /* null = */ handle_null, + /* boolean = */ handle_boolean, + /* integer = */ NULL, + /* double = */ NULL, + /* number = */ handle_number, + /* string = */ handle_string, + /* start map = */ handle_start_map, + /* map key = */ handle_string, + /* end map = */ handle_end_map, + /* start array = */ handle_start_array, + /* end array = */ handle_end_array + }; + + yajl_handle handle; + yajl_status status; + char * internal_err_str; + context_t ctx = { NULL, NULL, NULL, 0 }; + + ctx.errbuf = error_buffer; + ctx.errbuf_size = error_buffer_size; + + if (error_buffer != NULL) + memset (error_buffer, 0, error_buffer_size); + + handle = yajl_alloc (&callbacks, NULL, &ctx); + yajl_config(handle, yajl_allow_comments, 1); + + status = yajl_parse(handle, + (unsigned char *) input, + strlen (input)); + status = yajl_complete_parse (handle); + if (status != yajl_status_ok) { + if (error_buffer != NULL && error_buffer_size > 0) { + internal_err_str = (char *) yajl_get_error(handle, 1, + (const unsigned char *) input, + strlen(input)); + snprintf(error_buffer, error_buffer_size, "%s", internal_err_str); + YA_FREE(&(handle->alloc), internal_err_str); + } + yajl_free (handle); + return NULL; + } + + yajl_free (handle); + return (ctx.root); +} + +yajl_val yajl_tree_get(yajl_val n, const char ** path, yajl_type type) +{ + if (!path) return NULL; + while (n && *path) { + size_t i; + size_t len; + + if (n->type != yajl_t_object) return NULL; + len = n->u.object.len; + for (i = 0; i < len; i++) { + if (!strcmp(*path, n->u.object.keys[i])) { + n = n->u.object.values[i]; + break; + } + } + if (i == len) return NULL; + path++; + } + if (n && type != yajl_t_any && type != n->type) n = NULL; + return n; +} + +void yajl_tree_free (yajl_val v) +{ + if (v == NULL) return; + + if (YAJL_IS_STRING(v)) + { + free(v->u.string); + free(v); + } + else if (YAJL_IS_NUMBER(v)) + { + free(v->u.number.r); + free(v); + } + else if (YAJL_GET_OBJECT(v)) + { + yajl_object_free(v); + } + else if (YAJL_GET_ARRAY(v)) + { + yajl_array_free(v); + } + else /* if (yajl_t_true or yajl_t_false or yajl_t_null) */ + { + free(v); + } +} diff --git a/third-party/yajl/src/yajl_version.c b/third-party/yajl/src/yajl_version.c new file mode 100644 index 000000000..0671da722 --- /dev/null +++ b/third-party/yajl/src/yajl_version.c @@ -0,0 +1,7 @@ +#include + +int yajl_version(void) +{ + return YAJL_VERSION; +} +