diff options
Diffstat (limited to 'gpr/source/app')
24 files changed, 7835 insertions, 0 deletions
diff --git a/gpr/source/app/common/argument_parser/CMakeLists.txt b/gpr/source/app/common/argument_parser/CMakeLists.txt new file mode 100644 index 0000000..e965221 --- /dev/null +++ b/gpr/source/app/common/argument_parser/CMakeLists.txt @@ -0,0 +1,16 @@ +# library +set( LIB_NAME argument_parser ) + +# get source files +file( GLOB SRC_FILES "*.c" "*.cpp" ) + +# get include files +file( GLOB INC_FILES "*.h" ) + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/gpr/source/app/common/argument_parser/argument_parser.cpp b/gpr/source/app/common/argument_parser/argument_parser.cpp new file mode 100755 index 0000000..ead5a28 --- /dev/null +++ b/gpr/source/app/common/argument_parser/argument_parser.cpp @@ -0,0 +1,125 @@ +/*! @file argument_parser.cpp + * + * @brief Implement class to handle argument parsing + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "argument_parser.h" + +#include <stdio.h> + +using namespace std; + +#ifdef __GNUC__ +#define COMPILER "[GCC %d.%d.%d]", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ +#elif __INTEL_COMPILER +#define COMPILER "[ICC %d]", __INTEL_COMPILER +#elif _MSC_VER +#define COMPILER "[VS %d]", _MSC_VER +#else +#define COMPILER "[Unknown-CXX]" +#endif + +#ifdef _WIN32 +#define OPERATING_SYSTEM "[Windows]" +#elif __linux +#define OPERATING_SYSTEM "[Linux]" +#elif __CYGWIN__ +#define OPERATING_SYSTEM "[Cygwin]" +#elif __APPLE__ +#define OPERATING_SYSTEM "[Mac OS X]" +#else +#define OPERATING_SYSTEM "[Unknown-OS]" +#endif + +#define NUMBER_OF_BITS "[%d bit] ", (sizeof(void*) == 8 ? 64 : 32) ///< used for checking 64-bit O/S + +argument_parser::argument_parser(bool verbose) +{ +} + +void argument_parser::set_options() +{ +} + +int argument_parser::parse(int argc, char *argv [], const char* application_text, const char* prefix_text) +{ + argument_count = argc; + + for (int i = 0; i < argument_count; i++) + arguments[i] = argv[i]; + + set_options(); + + program_options_lite::setDefaults(command_options); + + const list<const char*>& argv_unhandled = program_options_lite::scanArgv(command_options, argument_count, (const char**) arguments); + + for (list<const char*>::const_iterator it = argv_unhandled.begin(); it != argv_unhandled.end(); it++) + { + fprintf(stderr, "Unhandled argument ignored: `%s'\n", *it); + } + + bool show_help = get_argument_count() == 1 || get_help(); + + if( get_verbose() || show_help ) + { + if( application_text ) + { + fprintf( stderr, "%s", application_text ); + fprintf( stderr, OPERATING_SYSTEM ); + fprintf( stderr, COMPILER ); + fprintf( stderr, NUMBER_OF_BITS ); + fprintf( stderr, "\n" ); + } + + printf("Executable: %s \n", get_application_path() ); + printf("Arguments: "); + + for (int i = 1; i < get_argument_count(); i++) + { + printf("%s ", get_argument(i)); + } + + printf("\n"); + } + + if ( show_help ) + { + print_help(); + return -1; + } + + if( application_text ) + { + if( prefix_text ) + fprintf( stderr, "%s %s", prefix_text, application_text ); + else + fprintf( stderr, "%s", application_text ); + + fprintf( stderr, OPERATING_SYSTEM ); + fprintf( stderr, COMPILER ); + fprintf( stderr, NUMBER_OF_BITS ); + fprintf( stderr, "\n" ); + } + + return 0; +} + +void argument_parser::print_help() +{ + doHelp(cout, command_options); +} + diff --git a/gpr/source/app/common/argument_parser/argument_parser.h b/gpr/source/app/common/argument_parser/argument_parser.h new file mode 100755 index 0000000..bc08a4c --- /dev/null +++ b/gpr/source/app/common/argument_parser/argument_parser.h @@ -0,0 +1,51 @@ +/*! @file argument_parser.h + * + * @brief Declare class to handle argument parsing + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define MAX_ARGC 100 + +#include "program_options_lite.h" + +class argument_parser +{ +private: + char* application_path; + int argument_count; + char* arguments[MAX_ARGC]; + +protected: + program_options_lite::Options command_options; + +public: + argument_parser(bool verbose = true); + + const int get_argument_count() { return argument_count; } + const char* get_argument(int index) { return arguments[index]; } + + const char* get_application_path() { return application_path; } + + virtual int parse(int argc, char *argv [], const char* application_text = NULL, const char* prefix_text = NULL ); + + virtual void set_options(); + + virtual void print_help(); + + virtual bool get_verbose() { return false; } + + virtual bool get_help() { return false; } + +}; diff --git a/gpr/source/app/common/argument_parser/program_options_lite.cpp b/gpr/source/app/common/argument_parser/program_options_lite.cpp new file mode 100755 index 0000000..ec3fbc5 --- /dev/null +++ b/gpr/source/app/common/argument_parser/program_options_lite.cpp @@ -0,0 +1,491 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2015, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdlib.h> +#include <iostream> +#include <fstream> +#include <sstream> +#include <string> +#include <list> +#include <map> +#include <algorithm> +#include "program_options_lite.h" + +using namespace std; + +namespace program_options_lite +{ + + Options::~Options() + { + for(Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); it++) + { + delete *it; + } + } + + void Options::addOption(OptionBase *opt) + { + Names* names = new Names(); + names->opt = opt; + string& opt_string = opt->opt_string; + + size_t opt_start = 0; + for (size_t opt_end = 0; opt_end != string::npos;) + { + opt_end = opt_string.find_first_of(',', opt_start); + bool force_short = 0; + if (opt_string[opt_start] == '-') + { + opt_start++; + force_short = 1; + } + string opt_name = opt_string.substr(opt_start, opt_end - opt_start); + if (force_short || opt_name.size() == 1) + { + names->opt_short.push_back(opt_name); + opt_short_map[opt_name].push_back(names); + } + else + { + names->opt_long.push_back(opt_name); + opt_long_map[opt_name].push_back(names); + } + opt_start += opt_end + 1; + } + opt_list.push_back(names); + } + + /* Helper method to initiate adding options to Options */ + OptionSpecific Options::addOptions() + { + return OptionSpecific(*this); + } + + static void setOptions(Options::NamesPtrList& opt_list, const string& value) + { + /* multiple options may be registered for the same name: + * allow each to parse value */ + for (Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); ++it) + { + (*it)->opt->parse(value); + } + } + + static const char spaces[41] = " "; + + /* format help text for a single option: + * using the formatting: "-x, --long", + * if a short/long option isn't specified, it is not printed + */ + static void doHelpOpt(ostream& out, const Options::Names& entry, unsigned pad_short = 0) + { + pad_short = min(pad_short, 8u); + + if (!entry.opt_short.empty()) + { + unsigned pad = max((int)pad_short - (int)entry.opt_short.front().size(), 0); + out << "-" << entry.opt_short.front(); + if (!entry.opt_long.empty()) + { + out << ", "; + } + out << &(spaces[40 - pad]); + } + else + { + out << " "; + out << &(spaces[40 - pad_short]); + } + + if (!entry.opt_long.empty()) + { + out << "--" << entry.opt_long.front(); + } + } + + /* format the help text */ + void doHelp(ostream& out, Options& opts, unsigned columns) + { + const unsigned pad_short = 3; + /* first pass: work out the longest option name */ + unsigned max_width = 0; + for(Options::NamesPtrList::iterator it = opts.opt_list.begin(); it != opts.opt_list.end(); it++) + { + ostringstream line(ios_base::out); + doHelpOpt(line, **it, pad_short); + max_width = max(max_width, (unsigned) line.tellp()); + } + + unsigned opt_width = min(max_width+2, 28u + pad_short) + 2; + unsigned desc_width = columns - opt_width; + + /* second pass: write out formatted option and help text. + * - align start of help text to start at opt_width + * - if the option text is longer than opt_width, place the help + * text at opt_width on the next line. + */ + for(Options::NamesPtrList::iterator it = opts.opt_list.begin(); it != opts.opt_list.end(); it++) + { + ostringstream line(ios_base::out); + line << " "; + doHelpOpt(line, **it, pad_short); + + const string& opt_desc = (*it)->opt->opt_desc; + if (opt_desc.empty()) + { + /* no help text: output option, skip further processing */ + cout << line.str() << endl; + continue; + } + size_t currlength = size_t(line.tellp()); + if (currlength > opt_width) + { + /* if option text is too long (and would collide with the + * help text, split onto next line */ + line << endl; + currlength = 0; + } + /* split up the help text, taking into account new lines, + * (add opt_width of padding to each new line) */ + for (size_t newline_pos = 0, cur_pos = 0; cur_pos != string::npos; currlength = 0) + { + /* print any required padding space for vertical alignment */ + line << &(spaces[40 - opt_width + currlength]); + newline_pos = opt_desc.find_first_of('\n', newline_pos); + if (newline_pos != string::npos) + { + /* newline found, print substring (newline needn't be stripped) */ + newline_pos++; + line << opt_desc.substr(cur_pos, newline_pos - cur_pos); + cur_pos = newline_pos; + continue; + } + if (cur_pos + desc_width > opt_desc.size()) + { + /* no need to wrap text, remainder is less than avaliable width */ + line << opt_desc.substr(cur_pos); + break; + } + /* find a suitable point to split text (avoid spliting in middle of word) */ + size_t split_pos = opt_desc.find_last_of(' ', cur_pos + desc_width); + if (split_pos != string::npos) + { + /* eat up multiple space characters */ + split_pos = opt_desc.find_last_not_of(' ', split_pos) + 1; + } + + /* bad split if no suitable space to split at. fall back to width */ + bool bad_split = split_pos == string::npos || split_pos <= cur_pos; + if (bad_split) + { + split_pos = cur_pos + desc_width; + } + line << opt_desc.substr(cur_pos, split_pos - cur_pos); + + /* eat up any space for the start of the next line */ + if (!bad_split) + { + split_pos = opt_desc.find_first_not_of(' ', split_pos); + } + cur_pos = newline_pos = split_pos; + + if (cur_pos >= opt_desc.size()) + { + break; + } + line << endl; + } + + cout << line.str() << endl; + } + } + + bool storePair(Options& opts, bool allow_long, bool allow_short, const string& name, const string& value) + { + bool found = false; + Options::NamesMap::iterator opt_it; + if (allow_long) + { + opt_it = opts.opt_long_map.find(name); + if (opt_it != opts.opt_long_map.end()) + { + found = true; + } + } + + /* check for the short list */ + if (allow_short && !(found && allow_long)) + { + opt_it = opts.opt_short_map.find(name); + if (opt_it != opts.opt_short_map.end()) + { + found = true; + } + } + + if (!found) + { + /* not found */ + cerr << "Unknown option: `" << name << "' (value:`" << value << "')" << endl; + return false; + } + + setOptions((*opt_it).second, value); + return true; + } + + bool storePair(Options& opts, const string& name, const string& value) + { + return storePair(opts, true, true, name, value); + } + + /** + * returns number of extra arguments consumed + */ + unsigned parseGNU(Options& opts, unsigned argc, const char* argv[]) + { + /* gnu style long options can take the forms: + * --option=arg + * --option arg + */ + string arg(argv[0]); + size_t arg_opt_start = arg.find_first_not_of('-'); + size_t arg_opt_sep = arg.find_first_of('='); + string option = arg.substr(arg_opt_start, arg_opt_sep - arg_opt_start); + + unsigned extra_argc_consumed = 0; + if (arg_opt_sep == string::npos) + { + /* no argument found => argument in argv[1] (maybe) */ + /* xxx, need to handle case where option isn't required */ +#if 0 + /* commented out, to return to true GNU style processing + * where longopts have to include an =, otherwise they are + * booleans */ + if (argc == 1) + { + return 0; /* run out of argv for argument */ + } + extra_argc_consumed = 1; +#endif + if(!storePair(opts, true, false, option, "1")) + { + return 0; + } + } + else + { + /* argument occurs after option_sep */ + string val = arg.substr(arg_opt_sep + 1); + storePair(opts, true, false, option, val); + } + + return extra_argc_consumed; + } + + unsigned parseSHORT(Options& opts, unsigned argc, const char* argv[]) + { + /* short options can take the forms: + * --option arg + * -option arg + */ + string arg(argv[0]); + size_t arg_opt_start = arg.find_first_not_of('-'); + string option = arg.substr(arg_opt_start); + /* lookup option */ + + /* argument in argv[1] */ + /* xxx, need to handle case where option isn't required */ + if (argc == 1) + { + cerr << "Not processing option without argument `" << option << "'" << endl; + return 0; /* run out of argv for argument */ + } + storePair(opts, false, true, option, string(argv[1])); + + return 1; + } + + list<const char*> + scanArgv(Options& opts, unsigned argc, const char* argv[]) + { + /* a list for anything that didn't get handled as an option */ + list<const char*> non_option_arguments; + + for(unsigned i = 1; i < argc; i++) + { + if (argv[i][0] != '-') + { + non_option_arguments.push_back(argv[i]); + continue; + } + + if (argv[i][1] == 0) + { + /* a lone single dash is an argument (usually signifying stdin) */ + non_option_arguments.push_back(argv[i]); + continue; + } + + if (argv[i][1] != '-') + { + /* handle short (single dash) options */ +#if 0 + i += parsePOSIX(opts, argc - i, &argv[i]); +#else + i += parseSHORT(opts, argc - i, &argv[i]); +#endif + continue; + } + + if (argv[i][2] == 0) + { + /* a lone double dash ends option processing */ + while (++i < argc) + { + non_option_arguments.push_back(argv[i]); + } + break; + } + + /* handle long (double dash) options */ + i += parseGNU(opts, argc - i, &argv[i]); + } + + return non_option_arguments; + } + + void scanLine(Options& opts, string& line) + { + /* strip any leading whitespace */ + size_t start = line.find_first_not_of(" \t\n\r"); + if (start == string::npos) + { + /* blank line */ + return; + } + if (line[start] == '#') + { + /* comment line */ + return; + } + /* look for first whitespace or ':' after the option end */ + size_t option_end = line.find_first_of(": \t\n\r",start); + string option = line.substr(start, option_end - start); + + /* look for ':', eat up any whitespace first */ + start = line.find_first_not_of(" \t\n\r", option_end); + if (start == string::npos) + { + /* error: badly formatted line */ + return; + } + if (line[start] != ':') + { + /* error: badly formatted line */ + return; + } + + /* look for start of value string -- eat up any leading whitespace */ + start = line.find_first_not_of(" \t\n\r", ++start); + if (start == string::npos) + { + /* error: badly formatted line */ + return; + } + + /* extract the value part, which may contain embedded spaces + * by searching for a word at a time, until we hit a comment or end of line */ + size_t value_end = start; + do + { + if (line[value_end] == '#') + { + /* rest of line is a comment */ + value_end--; + break; + } + value_end = line.find_first_of(" \t\n\r", value_end); + /* consume any white space, incase there is another word. + * any trailing whitespace will be removed shortly */ + value_end = line.find_first_not_of(" \t\n\r", value_end); + } while (value_end != string::npos); + /* strip any trailing space from value*/ + value_end = line.find_last_not_of(" \t\n\r", value_end); + + string value; + if (value_end >= start) + { + value = line.substr(start, value_end +1 - start); + } + else + { + /* error: no value */ + return; + } + + /* store the value in option */ + storePair(opts, true, false, option, value); + } + + void scanFile(Options& opts, istream& in) + { + do + { + string line; + getline(in, line); + scanLine(opts, line); + } while(!!in); + } + + /* for all options in opts, set their storage to their specified + * default value */ + void setDefaults(Options& opts) + { + for(Options::NamesPtrList::iterator it = opts.opt_list.begin(); it != opts.opt_list.end(); it++) + { + (*it)->opt->setDefault(); + } + } + + void parseConfigFile(Options& opts, const string& filename) + { + ifstream cfgstream(filename.c_str(), ifstream::in); + if (!cfgstream) + { + cerr << "Failed to open config file: `" << filename << "'" << endl; + exit(EXIT_FAILURE); + } + scanFile(opts, cfgstream); + } +} diff --git a/gpr/source/app/common/argument_parser/program_options_lite.h b/gpr/source/app/common/argument_parser/program_options_lite.h new file mode 100755 index 0000000..901c816 --- /dev/null +++ b/gpr/source/app/common/argument_parser/program_options_lite.h @@ -0,0 +1,231 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2015, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <iostream> +#include <sstream> +#include <string> +#include <list> +#include <map> + +#ifndef __PROGRAM_OPTIONS_LITE__ +#define __PROGRAM_OPTIONS_LITE__ + +namespace program_options_lite +{ + struct Options; + + struct ParseFailure : public std::exception + { + ParseFailure(std::string arg0, std::string val0) throw() + : arg(arg0), val(val0) + {} + + ~ParseFailure() throw() {}; + + std::string arg; + std::string val; + + const char* what() const throw() { return "Option Parse Failure"; } + }; + + void doHelp(std::ostream& out, Options& opts, unsigned columns = 80); + unsigned parseGNU(Options& opts, unsigned argc, const char* argv[]); + unsigned parseSHORT(Options& opts, unsigned argc, const char* argv[]); + std::list<const char*> scanArgv(Options& opts, unsigned argc, const char* argv[]); + void scanLine(Options& opts, std::string& line); + void scanFile(Options& opts, std::istream& in); + void setDefaults(Options& opts); + void parseConfigFile(Options& opts, const std::string& filename); + bool storePair(Options& opts, const std::string& name, const std::string& value); + + /** OptionBase: Virtual base class for storing information relating to a + * specific option This base class describes common elements. Type specific + * information should be stored in a derived class. */ + struct OptionBase + { + OptionBase(const std::string& name, const std::string& desc) + : opt_string(name), opt_desc(desc) + {}; + + virtual ~OptionBase() {} + + /* parse argument arg, to obtain a value for the option */ + virtual void parse(const std::string& arg) = 0; + /* set the argument to the default value */ + virtual void setDefault() = 0; + + std::string opt_string; + std::string opt_desc; + }; + + /** Type specific option storage */ + template<typename T> + struct Option : public OptionBase + { + Option(const std::string& name, T& storage, T default_val, const std::string& desc) + : OptionBase(name, desc), opt_storage(storage), opt_default_val(default_val) + {} + + void parse(const std::string& arg); + + void setDefault() + { + opt_storage = opt_default_val; + } + + T& opt_storage; + T opt_default_val; + }; + + /* Generic parsing */ + template<typename T> + inline void + Option<T>::parse(const std::string& arg) + { + std::istringstream arg_ss (arg,std::istringstream::in); + arg_ss.exceptions(std::ios::failbit); + try + { + arg_ss >> opt_storage; + } + catch (...) + { + throw ParseFailure(opt_string, arg); + } + } + + /* string parsing is specialized -- copy the whole string, not just the + * first word */ + template<> + inline void + Option<std::string>::parse(const std::string& arg) + { + opt_storage = arg; + } + + /** Option class for argument handling using a user provided function */ + struct OptionFunc : public OptionBase + { + typedef void (Func)(Options&, const std::string&); + + OptionFunc(const std::string& name, Options& parent_, Func *func_, const std::string& desc) + : OptionBase(name, desc), parent(parent_), func(func_) + {} + + void parse(const std::string& arg) + { + func(parent, arg); + } + + void setDefault() + { + return; + } + + private: + Options& parent; + void (*func)(Options&, const std::string&); + }; + + class OptionSpecific; + struct Options + { + ~Options(); + + OptionSpecific addOptions(); + + struct Names + { + Names() : opt(0) {}; + ~Names() + { + if (opt) + { + delete opt; + } + } + std::list<std::string> opt_long; + std::list<std::string> opt_short; + OptionBase* opt; + }; + + void addOption(OptionBase *opt); + + typedef std::list<Names*> NamesPtrList; + NamesPtrList opt_list; + + typedef std::map<std::string, NamesPtrList> NamesMap; + NamesMap opt_long_map; + NamesMap opt_short_map; + }; + + /* Class with templated overloaded operator(), for use by Options::addOptions() */ + class OptionSpecific + { + public: + OptionSpecific(Options& parent_) : parent(parent_) {} + + /** + * Add option described by name to the parent Options list, + * with storage for the option's value + * with default_val as the default value + * with desc as an optional help description + */ + template<typename T> + OptionSpecific& + operator()(const std::string& name, T& storage, T default_val, const std::string& desc = "") + { + parent.addOption(new Option<T>(name, storage, default_val, desc)); + return *this; + } + + /** + * Add option described by name to the parent Options list, + * with desc as an optional help description + * instead of storing the value somewhere, a function of type + * OptionFunc::Func is called. It is upto this function to correctly + * handle evaluating the option's value. + */ + OptionSpecific& + operator()(const std::string& name, OptionFunc::Func *func, const std::string& desc = "") + { + parent.addOption(new OptionFunc(name, parent, func, desc)); + return *this; + } + private: + Options& parent; + }; + +} /* namespace: program_options_lite */ + + +#endif // __PROGRAM_OPTIONS_LITE__ diff --git a/gpr/source/app/common/cJSON/CMakeLists.txt b/gpr/source/app/common/cJSON/CMakeLists.txt new file mode 100644 index 0000000..0c5348e --- /dev/null +++ b/gpr/source/app/common/cJSON/CMakeLists.txt @@ -0,0 +1,16 @@ +# library +set( LIB_NAME cJSON ) + +# get source files +file( GLOB SRC_FILES "*.c" "*.cpp" ) + +# get include files +file( GLOB INC_FILES "*.h" ) + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/gpr/source/app/common/cJSON/cJSON.c b/gpr/source/app/common/cJSON/cJSON.c new file mode 100755 index 0000000..aa6e563 --- /dev/null +++ b/gpr/source/app/common/cJSON/cJSON.c @@ -0,0 +1,2656 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + 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. */ + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif + +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <float.h> +#include <limits.h> +#include <ctype.h> +#include <locale.h> + +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 5) || (CJSON_VERSION_PATCH != 5) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(*allocate)(size_t size); + void (*deallocate)(void *pointer); + void *(*reallocate)(void *pointer, size_t size); +} internal_hooks; + +static internal_hooks global_hooks = { malloc, free, realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + if (!(copy = (unsigned char*)hooks->allocate(length))) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +#define cannot_read(buffer, size) (!can_read(buffer, size)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(&buffer))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + else + { + global_error = local_error; + } + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(256); + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->length); + buffer->buffer = NULL; + if (printed == NULL) { + goto fail; + } + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (len < 0) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + if (!output_buffer->noalloc) + { + output_buffer->hooks.deallocate(output_buffer->buffer); + } + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *c = array->child; + size_t i = 0; + while(c) + { + i++; + c = c->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)i; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* 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(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *ref = cJSON_New_Item(hooks); + if (!ref) + { + return NULL; + } + memcpy(ref, item, sizeof(cJSON)); + ref->string = NULL; + ref->type |= cJSON_IsReference; + ref->next = ref->prev = NULL; + return ref; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + /* call cJSON_AddItemToObjectCS for code reuse */ + cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item); + /* remove cJSON_StringIsConst flag */ + item->type &= ~cJSON_StringIsConst; +} + +#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + if (!item) + { + return; + } + if (!(item->type & cJSON_StringIsConst) && item->string) + { + global_hooks.deallocate(item->string); + } + item->string = (char*)string; + item->type |= cJSON_StringIsConst; + cJSON_AddItemToArray(object, item); +} +#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + cJSON_AddItemToArray(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + cJSON_AddItemToArray(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if (replacement == NULL) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + unsigned char *into = (unsigned char*)json; + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/gpr/source/app/common/cJSON/cJSON.h b/gpr/source/app/common/cJSON/cJSON.h new file mode 100755 index 0000000..2af0a9c --- /dev/null +++ b/gpr/source/app/common/cJSON/cJSON.h @@ -0,0 +1,263 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + 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 + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 5 +#define CJSON_VERSION_PATCH 5 + +#include <stddef.h> + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type __stdcall +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall +#endif +#else /* !WIN32 */ +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const 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. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(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. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + + +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Macros for creating things quickly. */ +#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_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#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)) +#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gpr/source/app/common/cJSON/cJSON_Utils.c b/gpr/source/app/common/cJSON/cJSON_Utils.c new file mode 100755 index 0000000..c0fd649 --- /dev/null +++ b/gpr/source/app/common/cJSON/cJSON_Utils.c @@ -0,0 +1,1388 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + 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. +*/ + +#pragma GCC visibility push(default) +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#pragma GCC visibility pop + +#include "cJSON_Utils.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +static unsigned char* cJSONUtils_strdup(const unsigned char* const string) +{ + size_t length = 0; + unsigned char *copy = NULL; + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*) cJSON_malloc(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +/* string comparison which doesn't consider NULL pointers equal */ +static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + if (case_sensitive) + { + return strcmp((const char*)string1, (const char*)string2); + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */ +static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive) +{ + if ((name == NULL) || (pointer == NULL)) + { + return false; + } + + for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */ + { + if (*pointer == '~') + { + /* check for escaped '~' (~0) and '/' (~1) */ + if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/'))) + { + /* invalid escape sequence or wrong character in *name */ + return false; + } + else + { + pointer++; + } + } + else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer))) + { + return false; + } + } + if (((*pointer != 0) && (*pointer != '/')) != (*name != 0)) + { + /* one string has ended, the other not */ + return false;; + } + + return true; +} + +/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */ +static size_t pointer_encoded_length(const unsigned char *string) +{ + size_t length; + for (length = 0; *string != '\0'; (void)string++, length++) + { + /* character needs to be escaped? */ + if ((*string == '~') || (*string == '/')) + { + length++; + } + } + + return length; +} + +/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */ +static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source) +{ + for (; source[0] != '\0'; (void)source++, destination++) + { + if (source[0] == '/') + { + destination[1] = '1'; + destination++; + } + else if (source[0] == '~') + { + destination[0] = '~'; + destination[1] = '1'; + destination++; + } + else + { + destination[0] = source[0]; + } + } + + destination[0] = '\0'; +} + +CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target) +{ + size_t child_index = 0; + cJSON *current_child = 0; + + if (object == target) + { + /* found */ + return (char*)cJSONUtils_strdup((const unsigned char*)""); + } + + /* recursively search all children of the object or array */ + for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++) + { + unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target); + /* found the target? */ + if (target_pointer != NULL) + { + if (cJSON_IsArray(object)) + { + /* reserve enough memory for a 64 bit integer + '/' and '\0' */ + unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/")); + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (child_index > ULONG_MAX) + { + cJSON_free(target_pointer); + return NULL; + } + sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* /<array_index><path> */ + cJSON_free(target_pointer); + + return (char*)full_pointer; + } + + if (cJSON_IsObject(object)) + { + unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2); + full_pointer[0] = '/'; + encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string); + strcat((char*)full_pointer, (char*)target_pointer); + cJSON_free(target_pointer); + + return (char*)full_pointer; + } + + /* reached leaf of the tree, found nothing */ + cJSON_free(target_pointer); + return NULL; + } + } + + /* not found */ + return NULL; +} + +/* non broken version of cJSON_GetArrayItem */ +static cJSON *get_array_item(const cJSON *array, size_t item) +{ + cJSON *child = array ? array->child : NULL; + while ((child != NULL) && (item > 0)) + { + item--; + child = child->next; + } + + return child; +} + +static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index) +{ + size_t parsed_index = 0; + size_t position = 0; + + if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/'))) + { + /* leading zeroes are not permitted */ + return 0; + } + + for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++) + { + parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0'); + + } + + if ((pointer[position] != '\0') && (pointer[position] != '/')) + { + return 0; + } + + *index = parsed_index; + + return 1; +} + +static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive) +{ + cJSON *current_element = object; + /* follow path of the pointer */ + while ((pointer[0] == '/') && (current_element != NULL)) + { + pointer++; + if (cJSON_IsArray(current_element)) + { + size_t index = 0; + if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index)) + { + return NULL; + } + + current_element = get_array_item(current_element, index); + } + else if (cJSON_IsObject(current_element)) + { + current_element = current_element->child; + /* GetObjectItem. */ + while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive)) + { + current_element = current_element->next; + } + } + else + { + return NULL; + } + + /* skip to the next path token or end of string */ + while ((pointer[0] != '\0') && (pointer[0] != '/')) + { + pointer++; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer) +{ + return get_item_from_pointer(object, pointer, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer) +{ + return get_item_from_pointer(object, pointer, true); +} + +/* JSON Patch implementation. */ +static void decode_pointer_inplace(unsigned char *string) +{ + unsigned char *decoded_string = string; + + if (string == NULL) { + return; + } + + for (; *string; (void)decoded_string++, string++) + { + if (string[0] == '~') + { + if (string[1] == '0') + { + decoded_string[0] = '~'; + } + else if (string[1] == '1') + { + decoded_string[1] = '/'; + } + else + { + /* invalid escape sequence */ + return; + } + + string++; + } + } + + decoded_string[0] = '\0'; +} + +/* non-broken cJSON_DetachItemFromArray */ +static cJSON *detach_item_from_array(cJSON *array, size_t which) +{ + cJSON *c = array->child; + while (c && (which > 0)) + { + c = c->next; + which--; + } + if (!c) + { + /* item doesn't exist */ + return NULL; + } + if (c->prev) + { + /* not the first element */ + c->prev->next = c->next; + } + if (c->next) + { + c->next->prev = c->prev; + } + if (c==array->child) + { + array->child = c->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + c->prev = c->next = NULL; + + return c; +} + +/* detach an item at the given path */ +static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive) +{ + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + cJSON *parent = NULL; + cJSON *detached_item = NULL; + + /* copy path and split it in parent and child */ + parent_pointer = cJSONUtils_strdup(path); + if (parent_pointer == NULL) { + goto cleanup; + } + + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */ + if (child_pointer == NULL) + { + goto cleanup; + } + /* split strings */ + child_pointer[0] = '\0'; + child_pointer++; + + parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); + decode_pointer_inplace(child_pointer); + + if (cJSON_IsArray(parent)) + { + size_t index = 0; + if (!decode_array_index_from_pointer(child_pointer, &index)) + { + goto cleanup; + } + detached_item = detach_item_from_array(parent, index); + } + else if (cJSON_IsObject(parent)) + { + detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer); + } + else + { + /* Couldn't find object to remove child from. */ + goto cleanup; + } + +cleanup: + if (parent_pointer != NULL) + { + cJSON_free(parent_pointer); + } + + return detached_item; +} + +/* sort lists using mergesort */ +static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) +{ + cJSON *first = list; + cJSON *second = list; + cJSON *current_item = list; + cJSON *result = list; + cJSON *result_tail = NULL; + + if ((list == NULL) || (list->next == NULL)) + { + /* One entry is sorted already. */ + return result; + } + + while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0)) + { + /* Test for list sorted. */ + current_item = current_item->next; + } + if ((current_item == NULL) || (current_item->next == NULL)) + { + /* Leave sorted lists unmodified. */ + return result; + } + + /* reset pointer to the beginning */ + current_item = list; + while (current_item != NULL) + { + /* Walk two pointers to find the middle. */ + second = second->next; + current_item = current_item->next; + /* advances current_item two steps at a time */ + if (current_item != NULL) + { + current_item = current_item->next; + } + } + if ((second != NULL) && (second->prev != NULL)) + { + /* Split the lists */ + second->prev->next = NULL; + } + + /* Recursively sort the sub-lists. */ + first = sort_list(first, case_sensitive); + second = sort_list(second, case_sensitive); + result = NULL; + + /* Merge the sub-lists */ + while ((first != NULL) && (second != NULL)) + { + cJSON *smaller = NULL; + if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, false) < 0) + { + smaller = first; + } + else + { + smaller = second; + } + + if (result == NULL) + { + /* start merged list with the smaller element */ + result_tail = smaller; + result = smaller; + } + else + { + /* add smaller element to the list */ + result_tail->next = smaller; + smaller->prev = result_tail; + result_tail = smaller; + } + + if (first == smaller) + { + first = first->next; + } + else + { + second = second->next; + } + } + + if (first != NULL) + { + /* Append rest of first list. */ + if (result == NULL) + { + return first; + } + result_tail->next = first; + first->prev = result_tail; + } + if (second != NULL) + { + /* Append rest of second list */ + if (result == NULL) + { + return second; + } + result_tail->next = second; + second->prev = result_tail; + } + + return result; +} + +static void sort_object(cJSON * const object, const cJSON_bool case_sensitive) +{ + object->child = sort_list(object->child, case_sensitive); +} + +static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + /* mismatched type. */ + return false; + } + switch (a->type & 0xFF) + { + case cJSON_Number: + /* numeric mismatch. */ + if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble)) + { + return false; + } + else + { + return true; + } + + case cJSON_String: + /* string mismatch. */ + if (strcmp(a->valuestring, b->valuestring) != 0) + { + return false; + } + else + { + return true; + } + + case cJSON_Array: + for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next) + { + cJSON_bool identical = compare_json(a, b, case_sensitive); + if (!identical) + { + return false; + } + } + + /* array size mismatch? (one of both children is not NULL) */ + if ((a != NULL) || (b != NULL)) + { + return false; + } + else + { + return true; + } + + case cJSON_Object: + sort_object(a, case_sensitive); + sort_object(b, case_sensitive); + for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next) + { + cJSON_bool identical = false; + /* compare object keys */ + if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive)) + { + /* missing member */ + return false; + } + identical = compare_json(a, b, case_sensitive); + if (!identical) + { + return false; + } + } + + /* object length mismatch (one of both children is not null) */ + if ((a != NULL) || (b != NULL)) + { + return false; + } + else + { + return true; + } + + default: + break; + } + + /* null, true or false */ + return true; +} + +/* non broken version of cJSON_InsertItemInArray */ +static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem) +{ + cJSON *child = array->child; + while (child && (which > 0)) + { + child = child->next; + which--; + } + if (which > 0) + { + /* item is after the end of the array */ + return 0; + } + if (child == NULL) + { + cJSON_AddItemToArray(array, newitem); + return 1; + } + + /* insert into the linked list */ + newitem->next = child; + newitem->prev = child->prev; + child->prev = newitem; + + /* was it at the beginning */ + if (child == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + + return 1; +} + +static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive) +{ + if (case_sensitive) + { + return cJSON_GetObjectItemCaseSensitive(object, name); + } + + return cJSON_GetObjectItem(object, name); +} + +enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST }; + +static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive) +{ + cJSON *operation = get_object_item(patch, "op", case_sensitive); + if (!cJSON_IsString(operation)) + { + return INVALID; + } + + if (strcmp(operation->valuestring, "add") == 0) + { + return ADD; + } + + if (strcmp(operation->valuestring, "remove") == 0) + { + return REMOVE; + } + + if (strcmp(operation->valuestring, "replace") == 0) + { + return REPLACE; + } + + if (strcmp(operation->valuestring, "move") == 0) + { + return MOVE; + } + + if (strcmp(operation->valuestring, "copy") == 0) + { + return COPY; + } + + if (strcmp(operation->valuestring, "test") == 0) + { + return TEST; + } + + return INVALID; +} + +/* overwrite and existing item with another one and free resources on the way */ +static void overwrite_item(cJSON * const root, const cJSON replacement) +{ + if (root == NULL) + { + return; + } + + if (root->string != NULL) + { + cJSON_free(root->string); + } + if (root->valuestring != NULL) + { + cJSON_free(root->valuestring); + } + if (root->child != NULL) + { + cJSON_Delete(root->child); + } + + memcpy(root, &replacement, sizeof(cJSON)); +} + +static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive) +{ + cJSON *path = NULL; + cJSON *value = NULL; + cJSON *parent = NULL; + enum patch_operation opcode = INVALID; + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + int status = 0; + + path = get_object_item(patch, "path", case_sensitive); + if (!cJSON_IsString(path)) + { + /* malformed patch. */ + status = 2; + goto cleanup; + } + + opcode = decode_patch_operation(patch, case_sensitive); + if (opcode == INVALID) + { + status = 3; + goto cleanup; + } + else if (opcode == TEST) + { + /* compare value: {...} with the given path */ + status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive); + goto cleanup; + } + + /* special case for replacing the root */ + if (path->valuestring[0] == '\0') + { + if (opcode == REMOVE) + { + static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL}; + + overwrite_item(object, invalid); + + status = 0; + goto cleanup; + } + + if ((opcode == REPLACE) || (opcode == ADD)) + { + value = get_object_item(patch, "value", case_sensitive); + if (value == NULL) + { + /* missing "value" for add/replace. */ + status = 7; + goto cleanup; + } + + value = cJSON_Duplicate(value, 1); + if (value == NULL) + { + /* out of memory for add/replace. */ + status = 8; + goto cleanup; + } + + overwrite_item(object, *value); + + /* delete the duplicated value */ + cJSON_free(value); + value = NULL; + + /* the string "value" isn't needed */ + if (object->string != NULL) + { + cJSON_free(object->string); + object->string = NULL; + } + + status = 0; + goto cleanup; + } + } + + if ((opcode == REMOVE) || (opcode == REPLACE)) + { + /* Get rid of old. */ + cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive); + if (old_item == NULL) + { + status = 13; + goto cleanup; + } + cJSON_Delete(old_item); + if (opcode == REMOVE) + { + /* For Remove, this job is done. */ + status = 0; + goto cleanup; + } + } + + /* Copy/Move uses "from". */ + if ((opcode == MOVE) || (opcode == COPY)) + { + cJSON *from = get_object_item(patch, "from", case_sensitive); + if (from == NULL) + { + /* missing "from" for copy/move. */ + status = 4; + goto cleanup; + } + + if (opcode == MOVE) + { + value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive); + } + if (opcode == COPY) + { + value = get_item_from_pointer(object, from->valuestring, case_sensitive); + } + if (value == NULL) + { + /* missing "from" for copy/move. */ + status = 5; + goto cleanup; + } + if (opcode == COPY) + { + value = cJSON_Duplicate(value, 1); + } + if (value == NULL) + { + /* out of memory for copy/move. */ + status = 6; + goto cleanup; + } + } + else /* Add/Replace uses "value". */ + { + value = get_object_item(patch, "value", case_sensitive); + if (value == NULL) + { + /* missing "value" for add/replace. */ + status = 7; + goto cleanup; + } + value = cJSON_Duplicate(value, 1); + if (value == NULL) + { + /* out of memory for add/replace. */ + status = 8; + goto cleanup; + } + } + + /* Now, just add "value" to "path". */ + + /* split pointer in parent and child */ + parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring); + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); + if (child_pointer != NULL) + { + child_pointer[0] = '\0'; + child_pointer++; + } + parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); + decode_pointer_inplace(child_pointer); + + /* add, remove, replace, move, copy, test. */ + if ((parent == NULL) || (child_pointer == NULL)) + { + /* Couldn't find object to add to. */ + status = 9; + goto cleanup; + } + else if (cJSON_IsArray(parent)) + { + if (strcmp((char*)child_pointer, "-") == 0) + { + cJSON_AddItemToArray(parent, value); + value = NULL; + } + else + { + size_t index = 0; + if (!decode_array_index_from_pointer(child_pointer, &index)) + { + status = 11; + goto cleanup; + } + + if (!insert_item_in_array(parent, index, value)) + { + status = 10; + goto cleanup; + } + value = NULL; + } + } + else if (cJSON_IsObject(parent)) + { + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer); + } + else + { + cJSON_DeleteItemFromObject(parent, (char*)child_pointer); + } + cJSON_AddItemToObject(parent, (char*)child_pointer, value); + value = NULL; + } + +cleanup: + if (value != NULL) + { + cJSON_Delete(value); + } + if (parent_pointer != NULL) + { + cJSON_free(parent_pointer); + } + + return status; +} + +CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches) +{ + const cJSON *current_patch = NULL; + int status = 0; + + if (!cJSON_IsArray(patches)) + { + /* malformed patches. */ + return 1; + } + + if (patches != NULL) + { + current_patch = patches->child; + } + + while (current_patch != NULL) + { + status = apply_patch(object, current_patch, false); + if (status != 0) + { + return status; + } + current_patch = current_patch->next; + } + + return 0; +} + +CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches) +{ + const cJSON *current_patch = NULL; + int status = 0; + + if (!cJSON_IsArray(patches)) + { + /* malformed patches. */ + return 1; + } + + if (patches != NULL) + { + current_patch = patches->child; + } + + while (current_patch != NULL) + { + status = apply_patch(object, current_patch, true); + if (status != 0) + { + return status; + } + current_patch = current_patch->next; + } + + return 0; +} + +static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value) +{ + cJSON *patch = cJSON_CreateObject(); + if (patch == NULL) + { + return; + } + cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation)); + + if (suffix == NULL) + { + cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path)); + } + else + { + size_t suffix_length = pointer_encoded_length(suffix); + size_t path_length = strlen((const char*)path); + unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/")); + + sprintf((char*)full_path, "%s/", (const char*)path); + encode_string_as_pointer(full_path + path_length + 1, suffix); + + cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path)); + cJSON_free(full_path); + } + + if (value != NULL) + { + cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1)); + } + cJSON_AddItemToArray(patches, patch); +} + +CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value) +{ + compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value); +} + +static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) +{ + if ((from == NULL) || (to == NULL)) + { + return; + } + + if ((from->type & 0xFF) != (to->type & 0xFF)) + { + compose_patch(patches, (const unsigned char*)"replace", path, 0, to); + return; + } + + switch (from->type & 0xFF) + { + case cJSON_Number: + if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble)) + { + compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); + } + return; + + case cJSON_String: + if (strcmp(from->valuestring, to->valuestring) != 0) + { + compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); + } + return; + + case cJSON_Array: + { + size_t index = 0; + cJSON *from_child = from->child; + cJSON *to_child = to->child; + unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */ + + /* generate patches for all array elements that exist in both "from" and "to" */ + for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++) + { + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (index > ULONG_MAX) + { + cJSON_free(new_path); + return; + } + sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */ + create_patches(patches, new_path, from_child, to_child, case_sensitive); + } + + /* remove leftover elements from 'from' that are not in 'to' */ + for (; (from_child != NULL); (void)(from_child = from_child->next)) + { + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (index > ULONG_MAX) + { + cJSON_free(new_path); + return; + } + sprintf((char*)new_path, "%lu", (unsigned long)index); + compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL); + } + /* add new elements in 'to' that were not in 'from' */ + for (; (to_child != NULL); (void)(to_child = to_child->next), index++) + { + compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child); + } + cJSON_free(new_path); + return; + } + + case cJSON_Object: + { + cJSON *from_child = NULL; + cJSON *to_child = NULL; + sort_object(from, case_sensitive); + sort_object(to, case_sensitive); + + from_child = from->child; + to_child = to->child; + /* for all object values in the object with more of them */ + while ((from_child != NULL) || (to_child != NULL)) + { + int diff; + if (from_child == NULL) + { + diff = 1; + } + else if (to_child == NULL) + { + diff = -1; + } + else + { + diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive); + } + + if (diff == 0) + { + /* both object keys are the same */ + size_t path_length = strlen((const char*)path); + size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string); + unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/")); + + sprintf((char*)new_path, "%s/", path); + encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string); + + /* create a patch for the element */ + create_patches(patches, new_path, from_child, to_child, case_sensitive); + cJSON_free(new_path); + + from_child = from_child->next; + to_child = to_child->next; + } + else if (diff < 0) + { + /* object element doesn't exist in 'to' --> remove it */ + compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL); + + from_child = from_child->next; + } + else + { + /* object element doesn't exist in 'from' --> add it */ + compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child); + + to_child = to_child->next; + } + } + return; + } + + default: + break; + } +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to) +{ + cJSON *patches = cJSON_CreateArray(); + create_patches(patches, (const unsigned char*)"", from, to, false); + + return patches; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to) +{ + cJSON *patches = cJSON_CreateArray(); + create_patches(patches, (const unsigned char*)"", from, to, true); + + return patches; +} + +CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object) +{ + sort_object(object, false); +} + +CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object) +{ + sort_object(object, true); +} + +static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive) +{ + cJSON *patch_child = NULL; + + if (!cJSON_IsObject(patch)) + { + /* scalar value, array or NULL, just duplicate */ + cJSON_Delete(target); + return cJSON_Duplicate(patch, 1); + } + + if (!cJSON_IsObject(target)) + { + cJSON_Delete(target); + target = cJSON_CreateObject(); + } + + patch_child = patch->child; + while (patch_child != NULL) + { + if (cJSON_IsNull(patch_child)) + { + /* NULL is the indicator to remove a value, see RFC7396 */ + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string); + } + else + { + cJSON_DeleteItemFromObject(target, patch_child->string); + } + } + else + { + cJSON *replace_me = NULL; + cJSON *replacement = NULL; + + if (case_sensitive) + { + replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string); + } + else + { + replace_me = cJSON_DetachItemFromObject(target, patch_child->string); + } + + replacement = merge_patch(replace_me, patch_child, case_sensitive); + if (replacement == NULL) + { + return NULL; + } + + cJSON_AddItemToObject(target, patch_child->string, replacement); + } + patch_child = patch_child->next; + } + return target; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch) +{ + return merge_patch(target, patch, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch) +{ + return merge_patch(target, patch, true); +} + +static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) +{ + cJSON *from_child = NULL; + cJSON *to_child = NULL; + cJSON *patch = NULL; + if (to == NULL) + { + /* patch to delete everything */ + return cJSON_CreateNull(); + } + if (!cJSON_IsObject(to) || !cJSON_IsObject(from)) + { + return cJSON_Duplicate(to, 1); + } + + sort_object(from, case_sensitive); + sort_object(to, case_sensitive); + + from_child = from->child; + to_child = to->child; + patch = cJSON_CreateObject(); + while (from_child || to_child) + { + int diff; + if (from_child != NULL) + { + if (to_child != NULL) + { + diff = strcmp(from_child->string, to_child->string); + } + else + { + diff = -1; + } + } + else + { + diff = 1; + } + + if (diff < 0) + { + /* from has a value that to doesn't have -> remove */ + cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull()); + + from_child = from_child->next; + } + else if (diff > 0) + { + /* to has a value that from doesn't have -> add to patch */ + cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1)); + + to_child = to_child->next; + } + else + { + /* object key exists in both objects */ + if (!compare_json(from_child, to_child, case_sensitive)) + { + /* not identical --> generate a patch */ + cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child)); + } + + /* next key in the object */ + from_child = from_child->next; + to_child = to_child->next; + } + } + if (patch->child == NULL) + { + /* no patch generated */ + cJSON_Delete(patch); + return NULL; + } + + return patch; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to) +{ + return generate_merge_patch(from, to, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to) +{ + return generate_merge_patch(from, to, true); +} diff --git a/gpr/source/app/common/cJSON/cJSON_Utils.h b/gpr/source/app/common/cJSON/cJSON_Utils.h new file mode 100755 index 0000000..03ec10c --- /dev/null +++ b/gpr/source/app/common/cJSON/cJSON_Utils.h @@ -0,0 +1,74 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + 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. +*/ + +#include "cJSON.h" + +/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer); +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer); + +/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */ +/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to); +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to); +/* Utility for generating patch array entries. */ +CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value); +/* Returns 0 for success. */ +CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches); +CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches); + +/* +// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use: +//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches) +//{ +// cJSON *modme = cJSON_Duplicate(*object, 1); +// int error = cJSONUtils_ApplyPatches(modme, patches); +// if (!error) +// { +// cJSON_Delete(*object); +// *object = modme; +// } +// else +// { +// cJSON_Delete(modme); +// } +// +// return error; +//} +// Code not added to library since this strategy is a LOT slower. +*/ + +/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */ +/* target will be modified by patch. return value is new ptr for target. */ +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch); +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch); +/* generates a patch to move from -> to */ +/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to); +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to); + +/* Given a root object and a target object, construct a pointer from one to the other. */ +CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target); + +/* Sorts the members of the object into alphabetical order. */ +CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object); +CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object); diff --git a/gpr/source/app/common/common_app_def.h b/gpr/source/app/common/common_app_def.h new file mode 100644 index 0000000..7095432 --- /dev/null +++ b/gpr/source/app/common/common_app_def.h @@ -0,0 +1,24 @@ +/*! @file common_app_def.h + * + * @brief Common defines for all sample applications + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMON_APP_DEFS_H +#define COMMON_APP_DEFS_H + +#define MAX_STDOUT_LINE 100 + +#endif diff --git a/gpr/source/app/gpr_tools/CMakeLists.txt b/gpr/source/app/gpr_tools/CMakeLists.txt new file mode 100644 index 0000000..ecf9828 --- /dev/null +++ b/gpr/source/app/gpr_tools/CMakeLists.txt @@ -0,0 +1,69 @@ +# executable +set( EXE_NAME gpr_tools ) + +# get source and include files +file( GLOB GPRTOOLS_SRC_FILES "*.c" "*.cpp" ) +file( GLOB GPRTOOLS_INC_FILES "*.h" "../common/*.h" ) + +# add include files from other folders +include_directories( "../common" ) +include_directories( "../common/cJSON" ) +include_directories( "../common/argument_parser" ) +include_directories( "../common/TinyJPEG" ) +include_directories( "../../lib/common/public" ) +include_directories( "../../lib/vc5_common" ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + include_directories( "../../lib/vc5_decoder" ) + add_definitions("-DGPR_READING=1") +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + add_definitions("-DGPR_READING=0") +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + include_directories( "../../lib/vc5_encoder" ) + add_definitions("-DGPR_WRITING=1") +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + add_definitions("-DGPR_WRITING=0") +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + +include_directories( "../../lib/md5_lib" ) +include_directories( "../../lib/dng_sdk" ) +include_directories( "../../lib/gpr_sdk/public" ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + include_directories( "../../lib/tiny_jpeg" ) + add_definitions("-DGPR_JPEG_AVAILABLE=1") +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + add_definitions("-DGPR_JPEG_AVAILABLE=0") +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + +# add executable +add_executable( ${EXE_NAME} ${GPRTOOLS_SRC_FILES} ${GPRTOOLS_INC_FILES} ) + +# Linked libraries +target_link_libraries( ${EXE_NAME} gpr_sdk ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + target_link_libraries( ${EXE_NAME} tiny_jpeg ) +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + +target_link_libraries( ${EXE_NAME} dng_sdk xmp_core ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + target_link_libraries( ${EXE_NAME} vc5_decoder ) +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + target_link_libraries( ${EXE_NAME} vc5_encoder ) +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + +target_link_libraries( ${EXE_NAME} vc5_common common md5_lib expat_lib cJSON argument_parser ) + +# In order to use Carbon API, define qEnableCarbon in gpr_platform.h and uncomment code below +# if (APPLE) +# target_link_libraries( ${EXE_NAME} "-framework Carbon" ) +# endif (APPLE) + +# set the folder where to place the projects +set_target_properties( ${EXE_NAME} PROPERTIES FOLDER app ) diff --git a/gpr/source/app/gpr_tools/gpr_parse_utils.cpp b/gpr/source/app/gpr_tools/gpr_parse_utils.cpp new file mode 100755 index 0000000..c31ca7d --- /dev/null +++ b/gpr/source/app/gpr_tools/gpr_parse_utils.cpp @@ -0,0 +1,545 @@ +/*! @file gpr_parse_utils.cpp + * + * @brief Parsing utilities for gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gpr_parse_utils.h" +#include "stdcpp_utils.h" + +#include "cJSON.h" + +#include "dng_stream.h" +#include "dng_misc_opcodes.h" +#include "dng_gain_map.h" + +#define MAX_BUF_SIZE 16000 + +void parse_gps_info( cJSON* pGpsInfo, gpr_gps_info& exif_info ) +{ + exif_info.gps_info_valid = false; +} + +void parse_exif_info( cJSON* pExifInfo, gpr_exif_info& exif_info ) +{ + cJSON* pJSON = pExifInfo->child; + + strcpy( exif_info.camera_make, pJSON->valuestring ); + pJSON = pJSON->next; + + strcpy( exif_info.camera_model, pJSON->valuestring ); + pJSON = pJSON->next; + + strcpy( exif_info.camera_serial, pJSON->valuestring ); + pJSON = pJSON->next; + + strcpy( exif_info.software_version, pJSON->valuestring ); + pJSON = pJSON->next; + + strcpy( exif_info.user_comment, pJSON->valuestring ); + pJSON = pJSON->next; + + strcpy( exif_info.image_description, pJSON->valuestring ); + pJSON = pJSON->next; + + exif_info.exposure_time.numerator = pJSON->child->valueint; + exif_info.exposure_time.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.f_stop_number.numerator = pJSON->child->valueint; + exif_info.f_stop_number.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.aperture.numerator = pJSON->child->valueint; + exif_info.aperture.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.exposure_program = (gpr_exposure_program)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.iso_speed_rating = pJSON->valueint; + pJSON = pJSON->next; + +// strcpy( exif_info.date_time_original, pJSON->valuestring ); + pJSON = pJSON->next; + +// strcpy( exif_info.date_time_digitized, pJSON->valuestring ); + pJSON = pJSON->next; + + exif_info.exposure_bias.numerator = pJSON->child->valueint; + exif_info.exposure_bias.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.light_source = (gpr_light_source)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.flash = (gpr_flash)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.focal_length.numerator = pJSON->child->valueint; + exif_info.focal_length.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.sharpness = (gpr_sharpness)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.saturation = pJSON->valueint; + pJSON = pJSON->next; + + exif_info.gain_control = (gpr_gain_control)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.contrast = (gpr_contrast)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.scene_capture_type = (gpr_scene_capture_type)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.exposure_mode = (gpr_exposure_mode)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.focal_length_in_35mm_film = pJSON->valueint; + pJSON = pJSON->next; + + exif_info.digital_zoom.numerator = pJSON->child->valueint; + exif_info.digital_zoom.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.white_balance = (gpr_white_balance)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.scene_type = (gpr_scene_type)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.file_source = (gpr_file_source)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.sensing_method = (gpr_sensing_method)pJSON->valueint; + pJSON = pJSON->next; + + parse_gps_info( pJSON, exif_info.gps_info ); +} + +void parse_profile_info( cJSON* pProfileInfo, gpr_profile_info& profile_info ) +{ + cJSON* pJSON = pProfileInfo->child; + + profile_info.compute_color_matrix = pJSON->valueint > 0 ? true : false; + pJSON = pJSON->next; + + profile_info.matrix_weighting = pJSON->valuedouble; + pJSON = pJSON->next; + + + { + cJSON* child = pJSON->child; + + profile_info.wb1[0] = child->valuedouble; + child = child->next; + + profile_info.wb1[1] = child->valuedouble; + child = child->next; + + profile_info.wb1[2] = child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + profile_info.wb2[0] = child->valuedouble; + child = child->next; + + profile_info.wb2[1] = child->valuedouble; + child = child->next; + + profile_info.wb2[2] = child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + profile_info.cam_to_srgb_1[0][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[0][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[0][2] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[1][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[1][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[1][2] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[2][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[2][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[2][2] = child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + profile_info.cam_to_srgb_2[0][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[0][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[0][2] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[1][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[1][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[1][2] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[2][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[2][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[2][2] = child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + profile_info.color_matrix_1[0][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[0][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[0][2] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[1][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[1][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[1][2] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[2][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[2][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[2][2] = child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + profile_info.color_matrix_2[0][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[0][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[0][2] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[1][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[1][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[1][2] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[2][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[2][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[2][2] = child->valuedouble; + + pJSON = pJSON->next; + } + + profile_info.illuminant1 = pJSON->valueint; + pJSON = pJSON->next; + + profile_info.illuminant2 = pJSON->valueint; +} + +void parse_tuning_info( cJSON* pTuningInfo, gpr_tuning_info& tuning_info ) +{ + cJSON* pJSON = pTuningInfo->child; + + tuning_info.orientation = (GPR_ORIENTATION)pJSON->valueint; + pJSON = pJSON->next; + + { + cJSON* child = pJSON->child; + tuning_info.static_black_level.r_black = child->valueint; + child = child->next; + + tuning_info.static_black_level.g_r_black = child->valueint; + child = child->next; + + tuning_info.static_black_level.g_b_black = child->valueint; + child = child->next; + + tuning_info.static_black_level.b_black = child->valueint; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + tuning_info.dgain_saturation_level.level_red = child->valueint; + child = child->next; + + tuning_info.dgain_saturation_level.level_green_even = child->valueint; + child = child->next; + + tuning_info.dgain_saturation_level.level_green_odd = child->valueint; + child = child->next; + + tuning_info.dgain_saturation_level.level_blue = child->valueint; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + tuning_info.wb_gains.r_gain = (float_t)child->valuedouble; + child = child->next; + + tuning_info.wb_gains.g_gain = (float_t)child->valuedouble; + child = child->next; + + tuning_info.wb_gains.b_gain = (float_t)child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + tuning_info.ae_info.iso_value = child->valueint; + child = child->next; + + tuning_info.ae_info.shutter_time = child->valueint; + + pJSON = pJSON->next; + } + + tuning_info.noise_scale = pJSON->valuedouble; + pJSON = pJSON->next; + + tuning_info.noise_offset = pJSON->valuedouble; + pJSON = pJSON->next; + + tuning_info.warp_red_coefficient = pJSON->valuedouble; + pJSON = pJSON->next; + + tuning_info.warp_blue_coefficient = pJSON->valuedouble; + pJSON = pJSON->next; + + if( pJSON->child ) + { + cJSON* size = pJSON->child; + int buffer_size = size->valueint; + + tuning_info.gain_map.size = buffer_size; + + cJSON* channel = size->next; + + int channel_index = 0; + while( channel && channel_index < 4 && buffer_size > 0 ) + { + cJSON* child = channel->child; + + int version = child->valueint; + child = child->next; + + int flags = child->valueint; + child = child->next; + + int bytes = child->valueint; + child = child->next; + + char gain_map_buffer[MAX_BUF_SIZE]; + + tuning_info.gain_map.buffers[channel_index] = (char*)malloc( buffer_size ); + + dng_stream gain_map_stream ( gain_map_buffer, buffer_size ); + + gain_map_stream.Put_uint32( version ); + gain_map_stream.Put_uint32( flags ); + gain_map_stream.Put_uint32( bytes ); + + { + cJSON* _child = child->child; + dng_rect rect; + rect.t = _child->valueint; + _child = _child->next; + + rect.l = _child->valueint; + _child = _child->next; + + rect.b = _child->valueint; + _child = _child->next; + + rect.r = _child->valueint; + + dng_area_spec area_spec(rect, 0, 1, 2, 2); + area_spec.PutData (gain_map_stream); + + child = child->next; + } + + + dng_point points; + + { + cJSON* _child = child->child; + + points.h = _child->valueint; + _child = _child->next; + + points.v = _child->valueint; + + child = child->next; + } + + dng_point_real64 spacing; + + { + cJSON* _child = child->child; + + spacing.h = _child->valuedouble; + _child = _child->next; + + spacing.v = _child->valuedouble; + + child = child->next; + } + + dng_point_real64 origin; + + { + cJSON* _child = child->child; + + origin.h = _child->valuedouble; + _child = _child->next; + + origin.v = _child->valuedouble; + + child = child->next; + } + + dng_gain_map gain_map( gDefaultDNGMemoryAllocator, points, spacing, origin, 1 ); + + cJSON* _child = child->child; + for (int row = 0; row < points.v; row++) + { + for (int col = 0; col < points.h; col++) + { + gain_map.Entry (row, col, 0) = (float_t)_child->valuedouble; + _child = _child->next; + } + } + + gain_map.PutStream( gain_map_stream ); + + memcpy( tuning_info.gain_map.buffers[channel_index], gain_map_buffer, buffer_size ); + + channel = channel->next; + + channel_index++; + } + } + + pJSON = pJSON->next; + + tuning_info.pixel_format = (GPR_PIXEL_FORMAT)pJSON->valueint; +} + +int gpr_parameters_parse( gpr_parameters* parameters, const char* input_file_path ) +{ + gpr_buffer buffer; + + if( read_from_file( &buffer, input_file_path, malloc, free) ) + { + return -2; + } + + const char* return_parse_end; + + cJSON* pRoot = cJSON_ParseWithOpts( (const char*)buffer.buffer, &return_parse_end, 0 ); + + if( pRoot == NULL ) + { + printf( "Error parsing %s \n", input_file_path ); + printf( "Error: %s", return_parse_end ); + return -1; + } + + cJSON* pJSON = pRoot->child; + + parameters->input_width = pJSON->valueint; + pJSON = pJSON->next; + + parameters->input_height = pJSON->valueint; + pJSON = pJSON->next; + + parameters->input_pitch = pJSON->valueint; + pJSON = pJSON->next; + + parameters->fast_encoding = pJSON->valueint > 0 ? true : false; + pJSON = pJSON->next; + + parameters->gpmf_payload.size = pJSON->valueint; + pJSON = pJSON->next; + + parse_exif_info( pJSON, parameters->exif_info ); + pJSON = pJSON->next; + + parse_profile_info( pJSON, parameters->profile_info ); + pJSON = pJSON->next; + + parse_tuning_info( pJSON, parameters->tuning_info ); + + free( buffer.buffer ); + + return 0; +} diff --git a/gpr/source/app/gpr_tools/gpr_parse_utils.h b/gpr/source/app/gpr_tools/gpr_parse_utils.h new file mode 100755 index 0000000..b4ebdaa --- /dev/null +++ b/gpr/source/app/gpr_tools/gpr_parse_utils.h @@ -0,0 +1,34 @@ +/*! @file gpr_parse_utils.h + * + * @brief Parsing utilities for gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_PARSE_UTILS_H +#define GPR_PARSE_UTILS_H + +#include "gpr.h" + +#ifdef __cplusplus +extern "C" { +#endif + + int gpr_parameters_parse( gpr_parameters* parameters, const char* input_file_path ); + +#ifdef __cplusplus +} +#endif + +#endif // GPR_PARSE_UTILS_H diff --git a/gpr/source/app/gpr_tools/gpr_print_utils.cpp b/gpr/source/app/gpr_tools/gpr_print_utils.cpp new file mode 100755 index 0000000..610db75 --- /dev/null +++ b/gpr/source/app/gpr_tools/gpr_print_utils.cpp @@ -0,0 +1,541 @@ +/*! @file gpr_print_utils.cpp + * + * @brief Printing utilities for gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gpr_print_utils.h" + +#include <iostream> +#include <fstream> +#include <algorithm> + +using namespace std; + +#include "dng_stream.h" +#include "dng_misc_opcodes.h" +#include "dng_gain_map.h" + +uint32 spaces = 0; + +ostream& operator<<(ostream& output, const gpr_signed_rational& x) +{ + output << "[" << x.numerator << "," << x.denominator << "]"; + + return output; +} + +ostream& operator<<(ostream& output, const gpr_unsigned_rational& x) +{ + output << "[" << x.numerator << "," << x.denominator << "]"; + + return output; +} + +ostream& operator<<(ostream& output, const gpr_date_and_time& x) +{ + output << "\"" << x.year << "-" << x.month << "-" << x.day << " " << x.hour << ":" << x.minute << ":" << x.second << "\""; + + return output; +} + +ostream& operator<<(ostream& output, const dng_area_spec& x) +{ + dng_rect area = x.Area(); + + output << "{ \"top\" : " << area.t << ", \"left\" : " << area.l << ", \"bottom\" : " << area.b << ", \"right\" : " << area.r << ", \"row_pitch\" : " << x.RowPitch() << ", \"col_pitch\" : " << x.ColPitch() << " }"; + + return output; +} + +ostream& operator<<(ostream& output, const dng_point& x) +{ + output << "{ \"h\" : " << x.h << ", \"v\" : " << x.v << " }"; + + return output; +} + +ostream& operator<<(ostream& output, const dng_point_real64& x) +{ + output << "{ \"h\" : " << x.h << ", \"v\" : " << x.v << " }"; + + return output; +} + +void start_tag( const string& tag_id, ostream& output ) +{ + output << "{" << endl; + spaces += 2; +} + +void end_tag( const string& tag_id, ostream& output ) +{ + spaces -= 2; + output << string( spaces, ' ' ).c_str() << "}"; +} + +template<class T> +void print_val(ostream& output, string tag, T x, int N = 0, bool last = false) +{ + if( last ) + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << x << "" << endl; + else + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << x << "," << endl; +} + +template<> +void print_val<const char*>(ostream& output, string tag, const char* x, int N, bool last) +{ + if( last ) + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << "\"" << x << "\"" << "" << endl; + else + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << "\"" << x << "\"" << "," << endl; +} + +template<> +void print_val<const double*>(ostream& output, string tag, const double* x, int N, bool last) +{ + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << "["; + + for (int i = 0; i < N; i++) + { + if( i < N - 1 ) + output << x[i] << ","; + else + output << x[i]; + } + + if( last ) + output << "]" << "" << endl; + else + output << "]" << "," << endl; +} + +template<> +void print_val<const gpr_unsigned_rational*>(ostream& output, string tag, const gpr_unsigned_rational* x, int N, bool last) +{ + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << "["; + + for (int i = 0; i < N; i++) + { + if( i < N - 1 ) + output << x[i] << ","; + else + output << x[i]; + } + + if( last ) + output << "]" << "" << endl; + else + output << "]" << "," << endl; +} + +template<class T, int N> +void print_val(ostream& output, string tag, T x[N][N]) +{ + output << string( spaces, ' ' ) << "\"" << tag << "\": " << "["; + output << x[0][0] << ","; + output << x[0][1] << ","; + output << x[0][2] << ","; + output << x[1][0] << ","; + output << x[1][1] << ","; + output << x[1][2] << ","; + output << x[2][0] << ","; + output << x[2][1] << ","; + output << x[2][2]; + output << "]"; + + output << "," << endl; +} + +ostream& operator<<(ostream& output, const gpr_gain_map& x) +{ + start_tag( "gain_map", output ); + + if( x.size > 0 ) + { + print_val( output, "size", x.size ); + + for (int i = 0; i < 4; i++) + { + dng_stream gain_map_stream (x.buffers[i], x.size); + + output << string( spaces, ' ' ).c_str() << "\"" << "channel_" << i << "\": "; + start_tag( "channel", output ); + + print_val( output, "version", gain_map_stream.Get_uint32() ); + + print_val( output, "flags", gain_map_stream.Get_uint32() ); + + print_val( output, "bytes", gain_map_stream.Get_uint32() ); + + dng_area_spec area_spec; + area_spec.GetData (gain_map_stream); + + print_val( output, "area", area_spec ); + + AutoPtr<dng_gain_map> gain_map; + + gain_map.Reset (dng_gain_map::GetStream (gain_map_stream, gDefaultDNGMemoryAllocator)); + + dng_point points = gain_map.Get()->Points(); + dng_point_real64 spacing = gain_map.Get()->Spacing(); + dng_point_real64 origin = gain_map.Get()->Origin(); + + print_val( output, "points", points ); + print_val( output, "spacing", spacing ); + print_val( output, "origin", origin ); + + output << string( spaces, ' ' ).c_str() << "\"" << "values" << "\": ["; + + for (int row = 0; row < points.v; row++) + { + for (int col = 0; col < points.h; col++) + { + output << gain_map->Entry (row, col, 0); + + if( row == points.v - 1 && col == points.h - 1 ) + output << " "; + else + output << ", "; + } + } + output << "] " << endl; + + end_tag( "channel", output ); + + if( i < 3 ) + output << ", " << endl; + } + } + + end_tag( "gain_map", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_gps_info& x) +{ + start_tag( "gps_info", output ); + + if( x.gps_info_valid ) + { + print_val( output, "gps_info_valid", x.gps_info_valid ); + + print_val( output, "version_id", x.version_id ); + + print_val( output, "latitude_ref", x.latitude_ref ); + + print_val( output, "latitude", x.latitude, 3 ); + + print_val( output, "longitude_ref", x.longitude_ref ); + + print_val( output, "longitude", x.longitude, 3 ); + + print_val( output, "altitude_ref", (uint32)x.altitude_ref ); + + print_val( output, "altitude", x.altitude ); + + print_val( output, "time_stamp", x.time_stamp ); + + print_val( output, "satellites", x.satellites ); + + print_val( output, "status", x.status ); + + print_val( output, "dop", x.dop ); + + print_val( output, "speed_ref", x.speed_ref ); + + print_val( output, "speed", x.speed ); + + print_val( output, "track_ref", x.track_ref ); + + print_val( output, "track", x.track ); + + print_val( output, "img_direction_ref", x.img_direction_ref ); + + print_val( output, "img_direction", x.img_direction ); + + print_val( output, "map_datum", x.map_datum ); + + print_val( output, "dest_latitude_ref", x.dest_latitude_ref ); + + print_val( output, "dest_latitude", x.dest_latitude ); + + print_val( output, "dest_longitude_ref", x.dest_longitude_ref ); + + print_val( output, "dest_longitude", x.dest_longitude ); + + print_val( output, "dest_bearing_ref", x.dest_bearing_ref ); + + print_val( output, "dest_bearing", x.dest_bearing ); + + print_val( output, "dest_distance_ref", x.dest_distance_ref ); + + print_val( output, "dest_distance", x.dest_distance ); + + print_val( output, "processing_method", x.processing_method ); + + print_val( output, "area_information", x.area_information ); + + print_val( output, "date_stamp", x.date_stamp ); + + print_val( output, "differential", x.differential, 0, true ); + } + else + { + print_val( output, "gps_info_valid", x.gps_info_valid, 0, true ); + } + + end_tag( "gps_info", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_exif_info& x) +{ + start_tag( "exif_info", output ); + + print_val( output, "camera_make", x.camera_make ); + + print_val( output, "camera_model", x.camera_model ); + + print_val( output, "camera_serial", x.camera_serial ); + + print_val( output, "software_version", x.software_version ); + + print_val( output, "user_comment", x.user_comment ); + + { + string str_image_description = x.image_description; + std::replace( str_image_description.begin(), str_image_description.end(), '\\', '/'); + print_val( output, "image_description", str_image_description.c_str() ); + } + + print_val( output, "exposure_time", x.exposure_time ); + + print_val( output, "f_stop_number", x.f_stop_number ); + + print_val( output, "aperture", x.aperture ); + + print_val( output, "exposure_program", x.exposure_program ); + + print_val( output, "iso_speed_rating", x.iso_speed_rating ); + + print_val( output, "date_time_original", x.date_time_original ); + + print_val( output, "date_time_digitized", x.date_time_digitized ); + + print_val( output, "exposure_bias", x.exposure_bias ); + + print_val( output, "light_source", x.light_source ); + + print_val( output, "flash", x.flash ); + + print_val( output, "focal_length", x.focal_length ); + + print_val( output, "sharpness", x.sharpness ); + + print_val( output, "saturation", x.saturation ); + + print_val( output, "gain_control", x.gain_control ); + + print_val( output, "contrast", x.contrast ); + + print_val( output, "scene_capture_type", x.scene_capture_type ); + + print_val( output, "exposure_mode", x.exposure_mode ); + + print_val( output, "focal_length_in_35mm_film", x.focal_length_in_35mm_film ); + + print_val( output, "digital_zoom", x.digital_zoom ); + + print_val( output, "white_balance", x.white_balance ); + + print_val( output, "scene_type", x.scene_type ); + + print_val( output, "file_source", x.file_source ); + + print_val( output, "sensing_method", x.sensing_method ); + + print_val( output, "gps_info", x.gps_info, 0, true ); + + end_tag( "exif_info", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_profile_info& x) +{ + start_tag( "profile_info", output ); + + print_val( output, "compute_color_matrix", x.compute_color_matrix ); + + print_val( output, "matrix_weighting", x.matrix_weighting ); + + print_val( output, "wb1", x.wb1, 3, false ); + + print_val( output, "wb2", x.wb2, 3, false ); + + print_val( output, "cam_to_srgb_1", (const double*)x.cam_to_srgb_1, 9, false ); + + print_val( output, "cam_to_srgb_2", (const double*)x.cam_to_srgb_2, 9, false ); + + print_val( output, "color_matrix_1", (const double*)x.color_matrix_1, 9, false ); + + print_val( output, "color_matrix_2", (const double*)x.color_matrix_2, 9, false ); + + print_val( output, "illuminant1", x.illuminant1, 0, false ); + + print_val( output, "illuminant2", x.illuminant2, 0, true ); + + end_tag( "profile_info", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_static_black_level& x) +{ + start_tag( "static_black_level", output ); + + print_val( output, "r_black", x.r_black ); + + print_val( output, "g_r_black", x.g_r_black ); + + print_val( output, "g_b_black", x.g_b_black ); + + print_val( output, "b_black", x.b_black, 0, true ); + + end_tag( "static_black_level", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_saturation_level& x) +{ + start_tag( "dgain_saturation_level", output ); + + print_val( output, "level_red", x.level_red ); + + print_val( output, "level_green_even", x.level_green_even ); + + print_val( output, "level_green_odd", x.level_green_odd ); + + print_val( output, "level_blue", x.level_blue, 0, true ); + + end_tag( "dgain_saturation_level", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_white_balance_gains& x) +{ + start_tag( "wb_gains", output ); + + print_val( output, "r_gain", x.r_gain ); + + print_val( output, "g_gain", x.g_gain ); + + print_val( output, "b_gain", x.b_gain, 0, true ); + + end_tag( "wb_gains", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_auto_exposure_info& x) +{ + start_tag( "ae_info", output ); + + print_val( output, "iso_value", x.iso_value ); + + print_val( output, "shutter_time", x.shutter_time, 0, true ); + + end_tag( "ae_info", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_tuning_info& x) +{ + start_tag( "tuning_info", output ); + + print_val( output, "orientation", x.orientation ); + + print_val( output, "static_black_level", x.static_black_level ); + + print_val( output, "dgain_saturation_level", x.dgain_saturation_level ); + + print_val( output, "wb_gains", x.wb_gains ); + + print_val( output, "ae_info", x.ae_info ); + + print_val( output, "noise_scale", x.noise_scale ); + + print_val( output, "noise_offset", x.noise_offset ); + + print_val( output, "warp_red_coefficient", x.warp_red_coefficient ); + + print_val( output, "warp_blue_coefficient", x.warp_blue_coefficient ); + + print_val( output, "gain_map", x.gain_map ); + + print_val( output, "pixel_format", x.pixel_format, 0, true ); + + end_tag( "tuning_info", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_parameters& x) +{ + print_val( output, "input_width", x.input_width ); + + print_val( output, "input_height", x.input_height ); + + print_val( output, "input_pitch", x.input_pitch ); + + print_val( output, "fast_encoding", x.fast_encoding ); + + // print_val( output, "gpmf_payload_buffer", x.gpmf_payload.buffer ); + + print_val( output, "gpmf_payload_size", x.gpmf_payload.size ); + + print_val( output, "exif_info", x.exif_info ); + + print_val( output, "profile_info", x.profile_info ); + + print_val( output, "tuning_info", x.tuning_info, 0, true ); + + return output; +} + +int gpr_parameters_print( const gpr_parameters* parameters, const char* output_file_path ) +{ + ofstream output; + ostream* output_ref = &cout; + + if( output_file_path ) + { + output.open (output_file_path); + output_ref = &output; + } + + start_tag( "", *output_ref ); + *output_ref << *parameters; + end_tag( "", *output_ref ); + + return 0; +} diff --git a/gpr/source/app/gpr_tools/gpr_print_utils.h b/gpr/source/app/gpr_tools/gpr_print_utils.h new file mode 100755 index 0000000..34dc63a --- /dev/null +++ b/gpr/source/app/gpr_tools/gpr_print_utils.h @@ -0,0 +1,34 @@ +/*! @file gpr_print_utils.h + * + * @brief Printing utilities for gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_PRINT_UTILS_H +#define GPR_PRINT_UTILS_H + +#include "gpr.h" + +#ifdef __cplusplus +extern "C" { +#endif + + int gpr_parameters_print( const gpr_parameters* parameters, const char* output_file_path ); + +#ifdef __cplusplus +} +#endif + +#endif // GPR_PRINT_UTILS_H diff --git a/gpr/source/app/gpr_tools/main.cpp b/gpr/source/app/gpr_tools/main.cpp new file mode 100755 index 0000000..e182028 --- /dev/null +++ b/gpr/source/app/gpr_tools/main.cpp @@ -0,0 +1,203 @@ +/*! @file main.cpp + * + * @brief Main program file for the gpr_tools. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <string.h> + +#include "argument_parser.h" + +#include "gpr.h" +#include "gpr_buffer.h" +#include "gpr_print_utils.h" +#include "main_c.h" + +#include "common_app_def.h" + +using namespace std; + +class my_argument_parser : public argument_parser +{ +protected: + + bool help; + + bool verbose; + +public: + + bool dump_gpr_parameters; + + string jpg_preview_file_path; + + int jpg_preview_file_width; + + int jpg_preview_file_height; + + int input_width; + + int input_height; + + int input_pitch; + + int input_skip_rows; + + string input_pixel_format; + + string input_file_path; + + string gpmf_file_path; + + string rgb_file_resolution; + + int rgb_file_bits; + + string output_file_path; + + string apply_gpr_parameters; + +public: + + bool get_verbose() { return verbose; } + + bool get_help() { return help; } + + void set_options() + { + command_options.addOptions() + /* long and short name */ /* variable to update */ /* default value */ /* help text */ + ("help", help, false, "Prints this help text") + + ("verbose", verbose, false, "Verbosity of the output") + + ("JpgPreviewFilePath,P", jpg_preview_file_path, string(""), "Preview jpg file path") + ("JpgPreviewFileWidth,W", jpg_preview_file_width, 0, "Preview jpg file width") + ("JpgPreviewFileHeight,H", jpg_preview_file_height, 0, "Preview jpg file height") + + ("DumpGprParameters,d", dump_gpr_parameters, false, "Dump GPR parameters to standard output") + + ("InputSkipRows,s", input_skip_rows, 0, "Input image rows to skip") + + ("InputFilePath,i", input_file_path, string(""), "Input file path.\n(files types: GPR, DNG, RAW)") + + ("InputWidth,w", input_width, 4000, "Input image width in pixel samples [4000]") + + ("InputHeight,h", input_height, 3000, "Input image height in pixel samples [3000]") + + ("InputPitch,p", input_pitch, 8000, "Input image pitch in bytes [8000]") + + ("InputPixelFormat,x", input_pixel_format, string("rggb14"), "Input pixel format \n(rggb12, rggb12p, [rggb14], gbrg12, gbrg12p)") + + ("ApplyGprParameters,a", apply_gpr_parameters, string(""), "Parameters to use for GPR or DNG file.") + + ("GPMFFilePath,g", gpmf_file_path, string(""), "GPMF file path") + + ("RgbFileResolution,r", rgb_file_resolution, string(""), "Output RGB resolution \n[1:1, 2:1, 4:1, 8:1. 16:1]") + ("RgbFileBits,b", rgb_file_bits, 8, "Output RGB bits [8]") + + ("OutputFilePath,o", output_file_path, string(""), "Output file path.\n(files types: GPR, DNG, PPM, RAW, JPG)"); + ; + } +}; + +int dng_dump(const char* input_file_path) +{ + gpr_allocator allocator; + allocator.Alloc = malloc; + allocator.Free = free; + + gpr_buffer input_buffer = { NULL, 0 }; + + gpr_parameters params; + + gpr_parameters_set_defaults(¶ms); + + if( read_from_file( &input_buffer, input_file_path, allocator.Alloc, allocator.Free ) != 0 ) + { + return -1; + } + + int success = gpr_parse_metadata( &allocator, &input_buffer, ¶ms ); + + if( success ) + { + gpr_parameters_print( ¶ms, NULL ); + } + + return 0; +} + +int main(int argc, char *argv []) +{ + my_argument_parser args; + + + char zerotag[MAX_STDOUT_LINE]; + sprintf(zerotag, "[%5d-ms] ", 0); + + char line[MAX_STDOUT_LINE]; + sprintf( line, "GPR Tools Version %d.%d.%d [%s @ %s] ", GPR_VERSION_MAJOR, GPR_VERSION_MINOR, GPR_VERSION_REVISION, GIT_BRANCH, GIT_COMMIT_HASH ); + + if( args.parse(argc, argv, line, zerotag) ) + { + printf("\n"); + printf("-- Example Commnads (please see data/tests/run_tests.sh for more examples) --\n"); + printf("GPR to DNG: \n"); + printf(" %s -i ./data/samples/Hero6/GOPR0024.GPR -o ./data/samples/Hero6/GOPR0024.DNG \n\n", argv[0] ); + printf("GPR to RGB (PPM format in 1000x750 resolution): \n"); + printf(" %s -i ./data/samples/Hero6/GOPR0024.GPR -o ./data/samples/Hero6/GOPR0024.PPM -r 4:1 \n\n", argv[0] ); + printf("GPR to RGB (JPG format in 500x375 resolution): \n"); + printf(" %s -i ./data/samples/Hero6/GOPR0024.GPR -o ./data/samples/Hero6/GOPR0024.JPG -r 8:1 \n\n", argv[0] ); + printf("Analyze a GPR or DNG file and output metadata parameters to a file: \n"); + printf(" %s -i ./data/samples/Hero6/GOPR0024.GPR -d 1 > ./data/samples/Hero6/GOPR0024.TXT \n\n", argv[0] ); + printf("Read RAW pixel data, along with gpr parameters (from a file) and apply to an output GPR or DNG file: \n"); + printf(" %s -i ./data/samples/Hero6/GOPR0024.RAW -o ./data/samples/Hero6/GOPR0024.DNG -a ./data/samples/Hero6/GOPR0024.TXT \n\n", argv[0] ); + + return -1; + } + + if( args.dump_gpr_parameters ) + { + if( dng_dump(args.input_file_path.c_str()) != 0 ) + return -1; + } + else + { + string ext = strrchr( args.input_file_path.c_str(),'.'); + + if( args.output_file_path == string("") && ( ext == string(".GPR") || ext == string(".gpr") ) ) + { + args.output_file_path = args.input_file_path; + args.output_file_path.erase(args.output_file_path.find_last_of("."), string::npos); + + args.output_file_path = args.output_file_path + string(".DNG"); + } + } + + fprintf( stderr, "%s Input File: %s \n", zerotag, args.input_file_path.c_str() ); + fprintf( stderr, "%s Output File: %s \n", zerotag, args.output_file_path.c_str() ); + + if( args.output_file_path != "" ) + { + return dng_convert_main(args.input_file_path.c_str(), args.input_width, args.input_height, args.input_pitch, args.input_skip_rows, args.input_pixel_format.c_str(), + args.output_file_path.c_str(), args.apply_gpr_parameters.c_str(), args.gpmf_file_path.c_str(), args.rgb_file_resolution.c_str(), args.rgb_file_bits, + args.jpg_preview_file_path.c_str(), args.jpg_preview_file_width, args.jpg_preview_file_height ); + } + + return 0; +} + diff --git a/gpr/source/app/gpr_tools/main_c.c b/gpr/source/app/gpr_tools/main_c.c new file mode 100755 index 0000000..0583b01 --- /dev/null +++ b/gpr/source/app/gpr_tools/main_c.c @@ -0,0 +1,346 @@ +/*! @file main_c.c + * + * @brief Implement C conversion routines used by gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <string.h> +#include <stdbool.h> + +#include "gpr.h" + +#if defined __GNUC__ +#define stricmp strcasecmp +#else +#endif // if defined __GNUC__ + +#include "main_c.h" +#include "gpr_parse_utils.h" +#include "gpr_print_utils.h" + +#if GPR_JPEG_AVAILABLE +#include "jpeg.h" +#endif + +#define MAX_FILE_PATH 256 + +typedef enum +{ + FILE_TYPE_UNKNOWN = -1, + + FILE_TYPE_RAW, + FILE_TYPE_GPR, + FILE_TYPE_DNG, + FILE_TYPE_PPM, + FILE_TYPE_JPG, + + FILE_TYPE_COUNT, + +} FILE_TYPE; + +static FILE_TYPE GetFileType( const char* file_path ) +{ + const char *extension = NULL; + + if (file_path == NULL) { + return FILE_TYPE_UNKNOWN; + } + + // Get the pathname extension + extension = strrchr(file_path, '.'); + if (extension == NULL) + { + return FILE_TYPE_UNKNOWN; + } + + if (stricmp(extension, ".raw") == 0 || stricmp(extension, ".RAW") == 0) + { + return FILE_TYPE_RAW; + } + + if (stricmp(extension, ".gpr") == 0 || stricmp(extension, ".GPR") == 0) + { + return FILE_TYPE_GPR; + } + + if (stricmp(extension, ".dng") == 0 || stricmp(extension, ".DNG") == 0 ) + { + return FILE_TYPE_DNG; + } + + if (stricmp(extension, ".ppm") == 0 || stricmp(extension, ".PPM") == 0) + { + return FILE_TYPE_PPM; + } + + if (stricmp(extension, ".jpg") == 0 || stricmp(extension, ".JPG") == 0) + { + return FILE_TYPE_JPG; + } + + return FILE_TYPE_UNKNOWN; +} + +int dng_convert_main(const char* input_file_path, unsigned int input_width, unsigned int input_height, size_t input_pitch, size_t input_skip_rows, const char* input_pixel_format, + const char* output_file_path, const char* metadata_file_path, const char* gpmf_file_path, const char* rgb_file_resolution, int rgb_file_bits, + const char* jpg_preview_file_path, int jpg_preview_file_width, int jpg_preview_file_height ) +{ + bool success; + bool write_buffer_to_file = true; + + FILE_TYPE input_file_type = GetFileType( input_file_path ); + FILE_TYPE output_file_type = GetFileType( output_file_path ); + + if( input_file_type == FILE_TYPE_UNKNOWN ) + { + printf( "Unsupported input file type" ); + return -1; + } + + if( output_file_type == FILE_TYPE_UNKNOWN ) + { + printf( "Unsupported output file type" ); + return -1; + } + + gpr_allocator allocator; + allocator.Alloc = malloc; + allocator.Free = free; + + gpr_parameters params; + gpr_parameters_set_defaults(¶ms); + + gpr_buffer input_buffer = { NULL, 0 }; + + if( read_from_file( &input_buffer, input_file_path, allocator.Alloc, allocator.Free ) != 0 ) + { + return -1; + } + + if( metadata_file_path && strcmp(metadata_file_path, "") ) + { + if( gpr_parameters_parse( ¶ms, metadata_file_path ) != 0 ) + return -1; + } + else if( input_file_type == FILE_TYPE_GPR || input_file_type == FILE_TYPE_DNG ) + { + gpr_parse_metadata( &allocator, &input_buffer, ¶ms ); + } + else + { + params.input_width = input_width; + params.input_height = input_height; + params.input_pitch = input_pitch; + + int32_t saturation_level = params.tuning_info.dgain_saturation_level.level_red; + + if( output_file_type == FILE_TYPE_GPR ) + saturation_level = (1 << 14) - 1; + else if( output_file_type == FILE_TYPE_DNG ) + saturation_level = (1 << 12) - 1; + + if( strcmp(input_pixel_format, "rggb12") == 0 ) + { + params.tuning_info.pixel_format = PIXEL_FORMAT_RGGB_12; + + if( input_pitch == -1 ) + input_pitch = input_width * 2; + } + if( strcmp(input_pixel_format, "rggb12p") == 0 ) + { + params.tuning_info.pixel_format = PIXEL_FORMAT_RGGB_12P; + + if( input_pitch == -1 ) + input_pitch = (input_width * 3 / 4) * 2; + } + else if( strcmp(input_pixel_format, "rggb14") == 0 ) + { + params.tuning_info.pixel_format = PIXEL_FORMAT_RGGB_14; + + saturation_level = (1 << 14) - 1; + + if( input_pitch == -1 ) + input_pitch = input_width * 2; + } + else if( strcmp(input_pixel_format, "gbrg12") == 0 ) + { + params.tuning_info.pixel_format = PIXEL_FORMAT_GBRG_12; + + if( input_pitch == -1 ) + input_pitch = input_width * 2; + } + else if( strcmp(input_pixel_format, "gbrg12p") == 0 ) + { + params.tuning_info.pixel_format = PIXEL_FORMAT_GBRG_12P; + + if( input_pitch == -1 ) + input_pitch = (input_width * 3 / 4) * 2; + } + + params.tuning_info.dgain_saturation_level.level_red = saturation_level; + params.tuning_info.dgain_saturation_level.level_green_even = saturation_level; + params.tuning_info.dgain_saturation_level.level_green_odd = saturation_level; + params.tuning_info.dgain_saturation_level.level_blue = saturation_level; + } + + if( gpmf_file_path != NULL && strcmp(gpmf_file_path, "") ) + { + read_from_file( ¶ms.gpmf_payload, gpmf_file_path, allocator.Alloc, allocator.Free ); + } + + gpr_buffer output_buffer = { NULL, 0 }; + + if( input_skip_rows > 0 ) + { + input_buffer.buffer = (unsigned char*)(input_buffer.buffer) + (input_skip_rows * input_pitch); + } + + gpr_buffer preview = { NULL, 0 }; + + if( strcmp(jpg_preview_file_path, "") != 0 ) + { + if( read_from_file( &preview, jpg_preview_file_path, allocator.Alloc, allocator.Free) == 0 ) + { + params.preview_image.jpg_preview = preview; + params.preview_image.preview_width = jpg_preview_file_width; + params.preview_image.preview_height = jpg_preview_file_height; + } + } + + if( input_file_type == FILE_TYPE_RAW && output_file_type == FILE_TYPE_DNG ) + { + success = gpr_convert_raw_to_dng( &allocator, ¶ms, &input_buffer, &output_buffer ); + } + else if( input_file_type == FILE_TYPE_DNG && output_file_type == FILE_TYPE_RAW ) + { + success = gpr_convert_dng_to_raw( &allocator, &input_buffer, &output_buffer ); + } + else if( input_file_type == FILE_TYPE_DNG && output_file_type == FILE_TYPE_DNG ) + { + success = gpr_convert_dng_to_dng( &allocator, ¶ms, &input_buffer, &output_buffer ); + } +#if GPR_WRITING + else if( input_file_type == FILE_TYPE_DNG && output_file_type == FILE_TYPE_GPR ) + { + success = gpr_convert_dng_to_gpr( &allocator, ¶ms, &input_buffer, &output_buffer ); + } + else if( input_file_type == FILE_TYPE_RAW && output_file_type == FILE_TYPE_GPR ) + { + success = gpr_convert_raw_to_gpr( &allocator, ¶ms, &input_buffer, &output_buffer ); + } +#endif +#if GPR_READING + else if( input_file_type == FILE_TYPE_GPR && ( output_file_type == FILE_TYPE_PPM || output_file_type == FILE_TYPE_JPG ) ) + { + gpr_rgb_buffer rgb_buffer = { NULL, 0, 0, 0 }; + + GPR_RGB_RESOLUTION rgb_resolution = GPR_RGB_RESOLUTION_DEFAULT; + + if( strcmp(rgb_file_resolution, "1:1") == 0 ) + rgb_resolution = GPR_RGB_RESOLUTION_FULL; + else if( strcmp(rgb_file_resolution, "2:1") == 0 ) + rgb_resolution = GPR_RGB_RESOLUTION_HALF; + else if( strcmp(rgb_file_resolution, "4:1") == 0 ) + rgb_resolution = GPR_RGB_RESOLUTION_QUARTER; + else if( strcmp(rgb_file_resolution, "8:1") == 0 ) + rgb_resolution = GPR_RGB_RESOLUTION_EIGHTH; + else if( strcmp(rgb_file_resolution, "16:1") == 0 ) + rgb_resolution = GPR_RGB_RESOLUTION_SIXTEENTH; + + if( output_file_type == FILE_TYPE_JPG && rgb_file_bits == 16 ) + { + printf( "Asked to output 16-bits RGB, but that is only possible in PPM format.\n"); + rgb_file_bits = 8; + } + + success = gpr_convert_gpr_to_rgb( &allocator, rgb_resolution, rgb_file_bits, &input_buffer, &rgb_buffer ); + + if( output_file_type == FILE_TYPE_PPM ) + { +#define PPM_HEADER_SIZE 100 + char header_text[PPM_HEADER_SIZE]; + + if( rgb_file_bits == 8 ) + { + // 8 bits + sprintf( header_text, "P6\n%ld %ld\n255\n", rgb_buffer.width, rgb_buffer.height ); + } + else + { + // 16 bits + sprintf( header_text, "P6\n%ld %ld\n65535\n", rgb_buffer.width, rgb_buffer.height ); + } + + output_buffer.size = rgb_buffer.size + strlen( header_text ); + output_buffer.buffer = allocator.Alloc( output_buffer.size ); + char* buffer_c = (char*)output_buffer.buffer; + + memcpy( buffer_c, header_text, strlen( header_text ) ); + memcpy( buffer_c + strlen( header_text ), rgb_buffer.buffer, rgb_buffer.size ); +#undef PPM_HEADER_SIZE + } + else if( output_file_type == FILE_TYPE_JPG ) + { + write_buffer_to_file = false; +#if GPR_JPEG_AVAILABLE + tje_encode_to_file( output_file_path, rgb_buffer.width, rgb_buffer.height, 3, rgb_buffer.buffer ); +#else + printf("JPG writing capability is disabled. You could still write to a PPM file"); +#endif + } + + allocator.Free( rgb_buffer.buffer ); + } + else if( input_file_type == FILE_TYPE_GPR && output_file_type == FILE_TYPE_DNG ) + { + success = gpr_convert_gpr_to_dng( &allocator, ¶ms, &input_buffer, &output_buffer ); + } + else if( input_file_type == FILE_TYPE_GPR && output_file_type == FILE_TYPE_RAW ) + { + success = gpr_convert_gpr_to_raw( &allocator, &input_buffer, &output_buffer ); + } +#endif + else + { + printf( "Unsupported conversion from %s to %s \n", input_file_path, output_file_path ); + return -1; + } + + if( success == 0 ) + { + printf("Conversion failed \n"); + return -1; + } + else if( write_buffer_to_file ) + { + write_to_file( &output_buffer, output_file_path ); + } + + if( input_skip_rows > 0 ) + { + input_buffer.buffer = (unsigned char*)(input_buffer.buffer) - (input_skip_rows * input_pitch); + } + + if( preview.buffer ) + { + allocator.Free( preview.buffer ); + } + + gpr_parameters_destroy(¶ms, allocator.Free); + + return 0; +} + diff --git a/gpr/source/app/gpr_tools/main_c.h b/gpr/source/app/gpr_tools/main_c.h new file mode 100755 index 0000000..a8bd308 --- /dev/null +++ b/gpr/source/app/gpr_tools/main_c.h @@ -0,0 +1,34 @@ +/*! @file main_c.h + * + * @brief Definition of C conversion routines used by gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MAIN_C_H +#define MAIN_C_H + +#ifdef __cplusplus +extern "C" { +#endif + + int dng_convert_main(const char* input_file_path, unsigned int input_width, unsigned int input_height, size_t input_pitch, size_t input_skip_rows, const char* input_pixel_format, + const char* output_file_path, const char* exiftool_file_path, const char* gpmf_file_path, const char* rgb_file_resolution, int rgb_file_bits, + const char* jpg_preview_file_path, int jpg_preview_file_width, int jpg_preview_file_height ); + +#ifdef __cplusplus +} +#endif + +#endif // MAIN_C_H diff --git a/gpr/source/app/gpr_tools/stdcpp_utils.h b/gpr/source/app/gpr_tools/stdcpp_utils.h new file mode 100755 index 0000000..eb7191d --- /dev/null +++ b/gpr/source/app/gpr_tools/stdcpp_utils.h @@ -0,0 +1,138 @@ +/*! @file stdcpp_utils.h + * + * @brief Implement some standard C++ routines using templates + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STDCPP_UTILS_H +#define STDCPP_UTILS_H + +#include <iostream> +#include <fstream> +#include <sstream> +#include <map> +#include <vector> +#include <algorithm> +#include <vector> +#include <functional> +#include <cctype> + +#include <stdio.h> +#include <string.h> + + std::string& ltrim(std::string& s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), + std::ptr_fun<int, int>(std::isgraph))); + return s; + } + + std::string& rtrim(std::string& s) + { + s.erase(std::find_if(s.rbegin(), s.rend(), + std::ptr_fun<int, int>(std::isgraph)).base(), s.end()); + return s; + } + + std::string& trim(std::string& s) + { + return ltrim(rtrim(s)); + } + + std::vector<std::string> tokenizer( const std::string& p_pcstStr, char delim ) { + std::vector<std::string> tokens; + std::stringstream mySstream( p_pcstStr ); + std::string temp; + + while( getline( mySstream, temp, delim ) ) { + tokens.push_back( temp ); + } + + return tokens; + } + + template<class T> + bool find_key( std::map<std::string, std::string> hash, const std::string key, T& value ) + { + std::map<std::string, std::string>::iterator it = hash.find( key.c_str() ); + if(it != hash.end()) + { + value = atoi( it->second.c_str() ); + + return true; + } + + return false; + } + + template<> + bool find_key<std::string>( std::map<std::string, std::string> hash, const std::string key, std::string& value ) + { + std::map<std::string, std::string>::iterator it = hash.find( key.c_str() ); + if(it != hash.end()) + { + value = it->second; + + return true; + } + + return false; + } + + template<class T> + bool find_key( std::map<std::string, std::string> hash, const std::string key, T& value_numerator, T& value_denominator ) + { + std::map<std::string, std::string>::iterator it = hash.find( key.c_str() ); + if(it != hash.end()) + { + std::string fraction = it->second; + std::vector<std::string> tokens = tokenizer( fraction, '/' ); + + value_numerator = atoi( tokens[0].c_str() ); + + if( tokens.size() == 2 ) + value_denominator = atoi( tokens[1].c_str() ); + else + value_denominator = 1; + + return true; + } + + return false; + } + + + template<class T> + static T parse_field(const char *tuning_sring, const char *field_name, const char* format_specifier, T default_val, bool* found = NULL ) + { + const char *foundStr = strstr(tuning_sring, field_name); + T ret_data; + + if (foundStr) + { + sscanf(foundStr + strlen(field_name), format_specifier, &ret_data); + + if(found) + *found = true; + + return ret_data; + } + + if(found) + *found = false; + + return default_val; + } + +#endif // STDCPP_UTILS_H diff --git a/gpr/source/app/vc5_decoder_app/CMakeLists.txt b/gpr/source/app/vc5_decoder_app/CMakeLists.txt new file mode 100644 index 0000000..4e2aa3c --- /dev/null +++ b/gpr/source/app/vc5_decoder_app/CMakeLists.txt @@ -0,0 +1,25 @@ +# executable +set( EXE_NAME vc5_decoder_app ) + +# get source and include files +file( GLOB DECODER_SRC_FILES "*.c" "*.cpp" ) +file( GLOB DECODER_INC_FILES "*.h" "../common/*.h" ) + +# add include files from other folders +include_directories( "../common" ) +include_directories( "../common/argument_parser" ) +include_directories( "../../lib/common/private" ) +include_directories( "../../lib/common/public" ) +include_directories( "../../lib/vc5_common" ) +include_directories( "../../lib/vc5_decoder" ) +include_directories( "../../lib/md5_lib" ) + +add_definitions("-DGPR_READING=1") + +# add executable +add_executable( ${EXE_NAME} ${DECODER_SRC_FILES} ${DECODER_INC_FILES} ${COMMON_SRC_FILES} ${COMMON_INC_FILES} ) + +target_link_libraries( ${EXE_NAME} vc5_decoder vc5_common common md5_lib argument_parser ) + +# set the folder where to place the projects +set_target_properties( ${EXE_NAME} PROPERTIES FOLDER app ) diff --git a/gpr/source/app/vc5_decoder_app/main.cpp b/gpr/source/app/vc5_decoder_app/main.cpp new file mode 100755 index 0000000..f9255ed --- /dev/null +++ b/gpr/source/app/vc5_decoder_app/main.cpp @@ -0,0 +1,223 @@ +/*! @file main.cpp + * + * @brief Main program file for the sample vc5 decoder. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdc_includes.h" +#include "stdcpp_includes.h" + +#if GPR_READING +#include "vc5_decoder.h" +#endif + +#include "argument_parser.h" +#include "timer.h" +#include "gpr_buffer.h" +#include "log.h" +#include "logcurve.h" +#include "common_app_def.h" + +#define DECODER_RUN_COUNT 1 + +using namespace std; + +class my_argument_parser : public argument_parser +{ +protected: + + bool help; + + bool verbose; + +public: + + string log_curve_file_path; + + string output_pixel_format; + + string input_file_path; + + string output_file_path; + +public: + + bool get_verbose() { return verbose; } + + bool get_help() { return help; } + + void set_options() + { + command_options.addOptions() + /* long and short name */ /* variable to update */ /* default value */ /* help text */ + ("help", help, false, "Prints this help text") + ("verbose", verbose, false, "Verbosity of the output") + + ("InputFilePath,i", input_file_path, string(""), "Input file path") + + ("OutputPixelFormat,x", output_pixel_format, string("rggb14"), "Output pixel format [rggb12, rggb12p, rggb14, gbrg12, gbrg12p]") + + ("OutputFilePath,o", output_file_path, string(""), "Output file path") + + ("PrintLogCurve,l", log_curve_file_path, string(""), "File for encoding log curve output"); + ; + } +}; + +/*! + @brief Main entry point for the reference decoder + + The program takes two arguments: the pathname to the file that contains a + sample to decode and the pathname to the output file for the decoded image. + + The input file should contain a single encoded sample without any header. + + The output file can be a DPX file, otherwise the decoded image is written to the output + file without a header. + + The image is decoded to the same dimensions as the encoded image and the decoded format + is the same format as the original source image input to the encoder. +*/ +int main(int argc, char *argv[]) +{ + my_argument_parser args; + + char line[MAX_STDOUT_LINE]; + sprintf( line, "VC5 Decoder Version %d.%d.%d [%s @ %s] ", VC5_VERSION_MAJOR, VC5_VERSION_MINOR, VC5_VERSION_REVISION, GIT_BRANCH, GIT_COMMIT_HASH ); + + if( args.parse(argc, argv, line, "[0000000000]" ) ) + return -1; + + int i; + + CODEC_ERROR error = CODEC_ERROR_OKAY; + + vc5_decoder_parameters vc5_decoder_params; + vc5_decoder_parameters_set_default(&vc5_decoder_params); + vc5_decoder_params.enabled_parts = VC5_ENABLED_PARTS; + vc5_decoder_params.mem_alloc = malloc; + vc5_decoder_params.mem_free = free; + + if( strcmp(args.output_pixel_format.c_str(), "rggb12") == 0 ) + { + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_RGGB_12; + } + else if( strcmp(args.output_pixel_format.c_str(), "rggb14") == 0 ) + { + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_RGGB_14; + } + else if( strcmp(args.output_pixel_format.c_str(), "gbrg12") == 0 ) + { + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_GBRG_12; + } + else if( strcmp(args.output_pixel_format.c_str(), "gbrg14") == 0 ) + { + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_GBRG_14; + } + else + { + LogPrint("Invalid output format: %s", args.output_pixel_format.c_str()); + return -1; + } + + LogInit(); + + gpr_buffer vc5_image = { NULL, 0 }; + + // Print the flags indicating which parts are enabled for this encoder + LogPrint("Vc5 Input image: %s", args.input_file_path.c_str() ); + LogPrint("Raw Output file: %s", args.output_file_path.c_str() ); + + if( read_from_file( &vc5_image, args.input_file_path.c_str(), vc5_decoder_params.mem_alloc, vc5_decoder_params.mem_free ) ) + { + LogPrint("Could not read input file: %s", args.input_file_path.c_str()); + exit(-1); + } + + TIMER timer; // Performance timer + InitTimer(&timer); + + for (i = 0; i < DECODER_RUN_COUNT; i++) + { // Decode all frames + + gpr_buffer raw_image = { NULL, 0 }; + gpr_rgb_buffer rgb_image = { NULL, 0, 0, 0 }; + + StartTimer(&timer); + + LogPrint("%d ", i); + + vc5_decoder_process( &vc5_decoder_params, &vc5_image, &raw_image, &rgb_image ); + + StopTimer(&timer); + + fflush(stdout); + + assert( raw_image.buffer && raw_image.size > 0 ); + + if( write_to_file( &raw_image, args.output_file_path.c_str() ) ) + { + LogPrint("Error writing bitstream to location %s", args.output_file_path.c_str() ); + return -1; + } + + if( raw_image.buffer ) + { + vc5_decoder_params.mem_free(raw_image.buffer); + } + + if( rgb_image.buffer ) + { + vc5_decoder_params.mem_free(rgb_image.buffer); + } + } + + LogPrint("Decoding %.3f secs per frame", TimeSecs(&timer) / DECODER_RUN_COUNT ); + + if( args.log_curve_file_path != "" ) + { + LogPrint("Printing log curve to %s", args.log_curve_file_path.c_str() ); + + ofstream file; + file.open ( args.log_curve_file_path.c_str() ); + + + for( int i = 0; i < LOG_CURVE_TABLE_LENGTH; i++ ) + { + file.fill( '0' ); + file.width( 4 ); + file << i; + + file << ": "; + + file.fill( '0' ); + file.width( 4 ); + file << (DecoderLogCurve[i] >> 4); + + file << endl; + } + + file.close(); + } + + if( vc5_image.buffer ) + { + vc5_decoder_params.mem_free( vc5_image.buffer ); + } + + LogUninit(); + + return error; +} diff --git a/gpr/source/app/vc5_encoder_app/CMakeLists.txt b/gpr/source/app/vc5_encoder_app/CMakeLists.txt new file mode 100644 index 0000000..8e24526 --- /dev/null +++ b/gpr/source/app/vc5_encoder_app/CMakeLists.txt @@ -0,0 +1,25 @@ +# executable +set( EXE_NAME vc5_encoder_app ) + +# get source and include files +file( GLOB ENCODER_SRC_FILES "*.c" "*.cpp" ) +file( GLOB ENCODER_INC_FILES "*.h" "../common/*.h" ) + +# add include files from other folders +include_directories( "../common" ) +include_directories( "../common/argument_parser" ) +include_directories( "../../lib/common/private" ) +include_directories( "../../lib/common/public" ) +include_directories( "../../lib/vc5_common" ) +include_directories( "../../lib/vc5_encoder" ) +include_directories( "../../lib/md5_lib" ) + +add_definitions("-DGPR_WRITING=1") + +# add executable +add_executable( ${EXE_NAME} ${ENCODER_SRC_FILES} ${ENCODER_INC_FILES} ${COMMON_SRC_FILES} ${COMMON_INC_FILES} ) + +target_link_libraries( ${EXE_NAME} vc5_encoder vc5_common common md5_lib argument_parser ) + +# set the folder where to place the projects +set_target_properties( ${EXE_NAME} PROPERTIES FOLDER app ) diff --git a/gpr/source/app/vc5_encoder_app/main.cpp b/gpr/source/app/vc5_encoder_app/main.cpp new file mode 100755 index 0000000..b82254d --- /dev/null +++ b/gpr/source/app/vc5_encoder_app/main.cpp @@ -0,0 +1,283 @@ +/*! @file main.cpp + * + * @brief Main program file for the sample vc5 encoder. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdc_includes.h" +#include "stdcpp_includes.h" + +#include "vc5_encoder.h" + +#include "argument_parser.h" +#include "timer.h" +#include "gpr_buffer.h" +#include "md5.h" +#include "log.h" +#include "logcurve.h" +#include "common_app_def.h" + +using namespace std; + +class my_argument_parser : public argument_parser +{ +protected: + + bool help; + + bool verbose; + +public: + + bool validate; + + bool dump_info; + + int input_width; + + int input_height; + + int input_pitch; + + int input_skip_rows; + + string log_curve_file_path; + + string input_pixel_format; + + string input_file_path; + + string output_file_path; + +public: + + bool get_verbose() { return verbose; } + + bool get_help() { return help; } + + void set_options() + { + command_options.addOptions() + /* long and short name */ /* variable to update */ /* default value */ /* help text */ + ("help", help, false, "Prints this help text") + ("verbose", verbose, false, "Verbosity of the output") + + ("InputFilePath,i", input_file_path, string(""), "Input file path") + + ("InputWidth,w", input_width, 4000, "Input image width in pixel samples e.g. 4000") + + ("InputHeight,h", input_height, 3000, "Input image height in pixel samples e.g. 3000") + + ("InputPitch,p", input_pitch, -1, "Input image pitch in bytes e.g. 8000") + + ("InputPixelFormat,x", input_pixel_format, string("rggb14"), "Input pixel format [rggb12, rggb12p, rggb14, gbrg12, gbrg12p]") + + ("OutputFilePath,o", output_file_path, string(""), "Output file path") + + ("PrintLogCurve,l", log_curve_file_path, string(""), "File for encoding log curve output"); + ; + } +}; + +/*! + @brief Main entry point for the reference encoder + + Usage: encoder [options] input output + + The input argument is the pathname to a file that contains a single image that + is the input the image unpacking process (see @ref ImageUnpackingProcess). The output argument is + the pathname to a file that will contain the encoded bitstream_file. Media containers are not + currently supported by the reference encoder. The command-line options are described in @ref ParseParameters. +*/ +int main(int argc, char *argv[]) +{ + my_argument_parser args; + + LogInit(); + + char line[MAX_STDOUT_LINE]; + sprintf( line, "VC5 Encoder Version %d.%d.%d [%s @ %s] ", VC5_VERSION_MAJOR, VC5_VERSION_MINOR, VC5_VERSION_REVISION, GIT_BRANCH, GIT_COMMIT_HASH ); + + if( args.parse(argc, argv, line, "[0000000000]") ) + return -1; + + int encoder_run; + int encoder_run_count = 1; + + CODEC_ERROR error = CODEC_ERROR_OKAY; + + vc5_encoder_parameters vc5_encoder_params; + vc5_encoder_parameters_set_default(&vc5_encoder_params); + + vc5_encoder_params.enabled_parts = VC5_ENABLED_PARTS; + vc5_encoder_params.input_width = args.input_width; + vc5_encoder_params.input_height = args.input_height; + vc5_encoder_params.input_pitch = args.input_pitch; + vc5_encoder_params.mem_alloc = malloc; + vc5_encoder_params.mem_free = free; + + if( strcmp(args.input_pixel_format.c_str(), "rggb12") == 0 ) + { + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_RGGB_12; + + if( args.input_pitch == -1 ) + vc5_encoder_params.input_pitch = args.input_width * 2; + } + if( strcmp(args.input_pixel_format.c_str(), "rggb12p") == 0 ) + { + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_RGGB_12P; + + if( args.input_pitch == -1 ) + vc5_encoder_params.input_pitch = (args.input_width * 3 / 4) * 2; + } + else if( strcmp(args.input_pixel_format.c_str(), "rggb14") == 0 ) + { + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_RGGB_14; + + if( args.input_pitch == -1 ) + args.input_pitch = args.input_width * 2; + } + else if( strcmp(args.input_pixel_format.c_str(), "gbrg12") == 0 ) + { + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_GBRG_12; + + if( args.input_pitch == -1 ) + args.input_pitch = args.input_width * 2; + } + else if( strcmp(args.input_pixel_format.c_str(), "gbrg12p") == 0 ) + { + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_GBRG_12P; + + if( args.input_pitch == -1 ) + args.input_pitch = (args.input_width * 3 / 4) * 2; + } + else + { + LogPrint("Invalid input format: %s", args.input_pixel_format.c_str()); + return -1; + } + + gpr_buffer raw_image = { NULL, 0 }; + + // Print the flags indicating which parts are enabled for this encoder + LogPrint("Raw Input image: %s", args.input_file_path.c_str() ); + LogPrint("Vc5 Output file: %s", args.output_file_path.c_str() ); + + if( read_from_file( &raw_image, args.input_file_path.c_str(), vc5_encoder_params.mem_alloc, vc5_encoder_params.mem_free ) ) + { + LogPrint("Could not read input file: %s", args.input_file_path.c_str()); + return -1; + } + + TIMER timer; + InitTimer(&timer); + + unsigned char old_digest[MD5_DIGEST_SIZE]; + + for (encoder_run = 0; encoder_run < encoder_run_count; encoder_run++) + { + gpr_buffer vc5_image = { NULL, 0 }; + + StartTimer(&timer); + + vc5_encoder_process( &vc5_encoder_params, &raw_image, &vc5_image, NULL ); + + StopTimer(&timer); + + assert( vc5_image.buffer && vc5_image.size > 0 ); + + if( write_to_file( &vc5_image, args.output_file_path.c_str() ) ) + { + LogPrint("Error writing bitstream to location %s", args.output_file_path.c_str() ); + return -1; + } + + { + static const char* hex = "0123456789ABCDEF"; + + unsigned char digest[MD5_DIGEST_SIZE]; + context_md5_t ctx; + + MD5Init(&ctx); + MD5Update(&ctx, (unsigned char *)vc5_image.buffer, vc5_image.size ); + MD5Final(digest, &ctx); + + { + int i; + unsigned char logged_digest[MD5_DIGEST_SIZE * 2 + 1]; + + for (i = 0; i < MD5_DIGEST_SIZE; i++) + { + logged_digest[i * 2 + 0] = hex[ digest[i] >> 4 ]; + logged_digest[i * 2 + 1] = hex[ digest[i] & 0xf ]; + } + + logged_digest[i * 2] = '\0'; + + LogPrint("%d %s", encoder_run, logged_digest); + } + + if( encoder_run > 0 ) + { + if( memcmp(old_digest, digest, sizeof(digest) ) ) + { + LogPrint("ERROR digests in run %d and %d do not match", encoder_run, encoder_run - 1 ); + return -1; + } + } + + memcpy(old_digest, digest, sizeof(digest)); + } + + vc5_encoder_params.mem_free(vc5_image.buffer); + } + + LogPrint("Encoding %.3f secs per frame", TimeSecs(&timer) / encoder_run_count ); + + if( args.log_curve_file_path != "" ) + { + LogPrint("Printing log curve to %s", args.log_curve_file_path.c_str() ); + + ofstream file; + file.open ( args.log_curve_file_path.c_str() ); + + + for( int i = 0; i < LOG_CURVE_TABLE_LENGTH; i++ ) + { + file.fill( '0' ); + file.width( 4 ); + file << i; + + file << ": "; + + file.fill( '0' ); + file.width( 4 ); + file << EncoderLogCurve[i]; + + file << endl; + } + + file.close(); + } + + if( raw_image.buffer ) + { + vc5_encoder_params.mem_free( raw_image.buffer ); + } + + LogUninit(); + + return error; +} |