Blob Blame History Raw
/*
 * Copyright (C) 2011-2014,2018 Red Hat, Inc.
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: tasleson
 *         Gris Ge <fge@redhat.com>
 */

#ifdef HAVE_YAJL_YAJL_VERSION_H
#include <yajl/yajl_version.h>
#endif

#if defined(HAVE_YAJL_YAJL_VERSION_H) && YAJL_MAJOR > 1
#define LSM_NEW_YAJL
#endif

Value::Value(void):t(null_t)
{
}

Value::Value(bool v):t(boolean_t), s((v) ? "true" : "false")
{
}

Value::Value(uint32_t v):t(numeric_t), s(to_string(v))
{
}

Value::Value(int32_t v):t(numeric_t), s(to_string(v))
{
}

Value::Value(uint64_t v):t(numeric_t), s(to_string(v))
{
}

Value::Value(int64_t v):t(numeric_t), s(to_string(v))
{
}

Value::Value(value_type type, const std::string & v):t(type), s(v)
{
}

Value::Value(const std::vector < Value > &v):t(array_t), array(v)
{
}

Value::Value(const char *v)
{
    if (v) {
        t = string_t;
        s = std::string(v);
    } else {
        t = null_t;
    }
}

Value::Value(const std::string & v):t(string_t), s(v)
{
}

Value::Value(const std::map < std::string, Value > &v):t(object_t), obj(v)
{
}

std::string Value::serialize(void)
{
    const unsigned char *buf;
    std::string json;

#ifdef LSM_NEW_YAJL
    size_t len;
    yajl_gen g = yajl_gen_alloc(NULL);
    if (g) {
        /* These could fail, but we will continue regardless */
        yajl_gen_config(g, yajl_gen_beautify, 1);
        yajl_gen_config(g, yajl_gen_indent_string, "  ");
    }
#else
    unsigned int len;
    yajl_gen_config conf = { 1, "  " };
    yajl_gen g = yajl_gen_alloc(&conf, NULL);
#endif

    if (g) {
        marshal(g);

        if (yajl_gen_status_ok == yajl_gen_get_buf(g, &buf, &len)) {
            json = std::string((const char *) buf);
        }
        yajl_gen_free(g);
    }
    return json;
}

Value::value_type Value::valueType() const
{
    return t;
}

Value & Value::operator[](const std::string & key) {
    if (t == object_t) {
        return obj[key];
    }
    throw ValueException("Value not object");
}

Value & Value::operator[](uint32_t i) {
    if (t == array_t) {
        return array[i];
    }
    throw ValueException("Value not array");
}

bool Value::hasKey(const std::string & k)
{
    if (t == object_t) {
        std::map < std::string, Value >::iterator iter = obj.find(k);
        if (iter != obj.end() && iter->first == k) {
            return true;
        }
    }
    return false;
}

bool Value::isValidRequest()
{
    return (t == Value::object_t && hasKey("method") &&
            hasKey("id") && hasKey("params"));
}

Value Value::getValue(const char *key)
{
    if (hasKey(key)) {
        return obj[key];
    }
    return Value();
}

bool Value::asBool()
{
    if (t == boolean_t) {
        return (s == "true");
    }
    throw ValueException("Value not boolean");
}

int32_t Value::asInt32_t()
{
    if (t == numeric_t) {
        int32_t rc;

        if (sscanf(s.c_str(), "%d", &rc) > 0) {
            return rc;
        }
        throw ValueException("Value not int32");
    }
    throw ValueException("Value not numeric");
}

int64_t Value::asInt64_t()
{
    if (t == numeric_t) {
        int64_t rc;
        if (sscanf(s.c_str(), "%lld", (long long int *) &rc) > 0) {
            return rc;
        }
        throw ValueException("Not an integer");
    }
    throw ValueException("Value not numeric");
}

uint32_t Value::asUint32_t()
{
    if (t == numeric_t) {
        uint32_t rc;
        if (sscanf(s.c_str(), "%u", &rc) > 0) {
            return rc;
        }
        throw ValueException("Not an integer");
    }
    throw ValueException("Value not numeric");
}

uint64_t Value::asUint64_t()
{
    if (t == numeric_t) {
        uint64_t rc;
        if (sscanf(s.c_str(), "%llu", (long long unsigned int *) &rc) > 0) {
            return rc;
        }
        throw ValueException("Not an integer");
    }
    throw ValueException("Value not numeric");
}

std::string Value::asString()
{
    if (t == string_t) {
        return s;
    } else if (t == null_t) {
        return std::string();
    }
    throw ValueException("Value not string");
}

const char *Value::asC_str()
{
    if (t == string_t) {
        return s.c_str();
    } else if (t == null_t) {
        return NULL;
    }
    throw ValueException("Value not string");
}

std::map < std::string, Value > Value::asObject()
{
    if (t == object_t) {
        return obj;
    }
    throw ValueException("Value not object");
}

std::vector < Value > Value::asArray()
{
    if (t == array_t) {
        return array;
    }
    throw ValueException("Value not array");
}

void Value::marshal(yajl_gen g)
{
    switch (t) {
    case (null_t):
        {
            if (yajl_gen_status_ok != yajl_gen_null(g)) {
                throw ValueException("yajl_gen_null failure");
            }
            break;
        }
    case (boolean_t):
        {
            if (yajl_gen_status_ok != yajl_gen_bool(g, (s == "true") ? 1 : 0)) {
                throw ValueException("yajl_gen_bool failure");
            }
            break;
        }
    case (string_t):
        {
            if (yajl_gen_status_ok !=
                yajl_gen_string(g, (const unsigned char *) s.c_str(),
                                s.size())) {
                throw ValueException("yajl_gen_string failure");
            }
            break;
        }
    case (numeric_t):
        {
            if (yajl_gen_status_ok != yajl_gen_number(g, s.c_str(), s.size())) {
                throw ValueException("yajl_gen_number failure");
            }
            break;
        }
    case (object_t):
        {

            if (yajl_gen_status_ok != yajl_gen_map_open(g)) {
                throw ValueException("yajl_gen_map_open failure");
            }

            std::map < std::string, Value >::iterator iter;

            for (iter = obj.begin(); iter != obj.end(); iter++) {
                if (yajl_gen_status_ok != yajl_gen_string(g,
                                                          (const unsigned
                                                           char *) iter->first.
                                                          c_str(),
                                                          iter->first.size())) {
                    throw ValueException("yajl_gen_string failure");
                }
                iter->second.marshal(g);
            }

            if (yajl_gen_status_ok != yajl_gen_map_close(g)) {
                throw ValueException("yajl_gen_map_close failure");
            }
            break;
        }
    case (array_t):
        {
            if (yajl_gen_status_ok != yajl_gen_array_open(g)) {
                throw ValueException("yajl_gen_array_open failure");
            }

            for (unsigned int i = 0; i < array.size(); ++i) {
                array[i].marshal(g);
            }

            if (yajl_gen_status_ok != yajl_gen_array_close(g)) {
                throw ValueException("yajl_gen_array_close failure");
            }
            break;
        }
    }
}

class LSM_DLL_LOCAL ParseElement {
  public:

    enum parse_type {
        null, boolean, string, number, begin_map, end_map,
        begin_array, end_array, map_key, unknown
    };

    ParseElement():t(unknown) {
    } ParseElement(parse_type type):t(type) {
    }

  ParseElement(parse_type type, std::string value):t(type), v(value) {
    }
    parse_type t;
    std::string v;

    std::string to_string() {
        return "type " +::to_string(t) + ": value" + v;
    }
};

#ifdef LSM_NEW_YAJL
#define YAJL_SIZE_T size_t
#else
#define YAJL_SIZE_T unsigned int
#endif

static int handle_value(void *ctx, ParseElement::parse_type type)
{
    std::list < ParseElement > *l = (std::list < ParseElement > *)ctx;
    l->push_back(ParseElement(type));
    return 1;
}

static int handle_value(void *ctx, ParseElement::parse_type type,
                        const char *s, size_t len)
{
    std::list < ParseElement > *l = (std::list < ParseElement > *)ctx;
    l->push_back(ParseElement(type, std::string(s, len)));
    return 1;
}

static int handle_null(void *ctx)
{
    return handle_value(ctx, ParseElement::null);
}

static int handle_boolean(void *ctx, int boolean)
{
    std::string b = (boolean) ? "true" : "false";
    return handle_value(ctx, ParseElement::boolean, b.c_str(), b.size());
}

static int handle_number(void *ctx, const char *s, YAJL_SIZE_T len)
{
    return handle_value(ctx, ParseElement::number, s, len);
}

static int handle_string(void *ctx, const unsigned char *stringVal,
                         YAJL_SIZE_T len)
{
    return handle_value(ctx, ParseElement::string,
                        (const char *) stringVal, len);
}

static int handle_map_key(void *ctx, const unsigned char *stringVal,
                          YAJL_SIZE_T len)
{
    return handle_value(ctx, ParseElement::map_key,
                        (const char *) stringVal, len);
}

static int handle_start_map(void *ctx)
{
    return handle_value(ctx, ParseElement::begin_map);
}

static int handle_end_map(void *ctx)
{
    return handle_value(ctx, ParseElement::end_map);
}

static int handle_start_array(void *ctx)
{
    return handle_value(ctx, ParseElement::begin_array);
}

static int handle_end_array(void *ctx)
{
    return handle_value(ctx, ParseElement::end_array);
}

static yajl_callbacks callbacks = {
    handle_null,
    handle_boolean,
    NULL,
    NULL,
    handle_number,
    handle_string,
    handle_start_map,
    handle_map_key,
    handle_end_map,
    handle_start_array,
    handle_end_array
};

static ParseElement get_next(std::list < ParseElement > &l)
{
    ParseElement rc = l.front();
    l.pop_front();
    return rc;
}

static Value ParseElements(std::list < ParseElement > &l);

static Value HandleArray(std::list < ParseElement > &l)
{
    std::vector < Value > values;

    ParseElement cur;

    if (!l.empty()) {
        do {
            cur = l.front();

            if (cur.t != ParseElement::end_array) {
                values.push_back(ParseElements(l));
            } else {
                get_next(l);
            }

        } while (!l.empty() && cur.t != ParseElement::end_array);
    }

    return Value(values);
}

static Value HandleObject(std::list < ParseElement > &l)
{
    std::map < std::string, Value > values;
    ParseElement cur;

    if (!l.empty()) {
        do {
            cur = get_next(l);

            if (cur.t == ParseElement::map_key) {
                values[cur.v] = ParseElements(l);
            } else if (cur.t != ParseElement::end_map) {
                throw ValueException("Unexpected state: " + cur.to_string());
            }
        } while (!l.empty() && cur.t != ParseElement::end_map);
    }
    return Value(values);
}

static Value ParseElements(std::list < ParseElement > &l)
{
    if (!l.empty()) {
        ParseElement cur = get_next(l);

        switch (cur.t) {
        case (ParseElement::null):
        case (ParseElement::boolean):
        case (ParseElement::string):
        case (ParseElement::number):
            {
                return Value((Value::value_type) cur.t, cur.v);
                break;
            }
        case (ParseElement::begin_map):
            {
                return HandleObject(l);
                break;
            }
        case (ParseElement::end_map):
            {
                throw ValueException("Unexpected end_map");
                break;
            }
        case (ParseElement::begin_array):
            {
                return HandleArray(l);
                break;
            }
        case (ParseElement::end_array):
            {
                throw ValueException("Unexpected end_array");
                break;
            }
        case (ParseElement::map_key):
            {
                throw ValueException("Unexpected map_key");
                break;
            }
        case (ParseElement::unknown):
            {
                throw ValueException("Unexpected unknown");
                break;
            }
        }
    }
    return Value();
}

std::string Payload::serialize(Value & v)
{
    return v.serialize();
}

Value Payload::deserialize(const std::string & json)
{
    yajl_handle hand;
    yajl_status stat;
    std::list < ParseElement > l;

#ifdef LSM_NEW_YAJL
    hand = yajl_alloc(&callbacks, NULL, (void *) &l);
    yajl_config(hand, yajl_allow_comments, 1);
#else
    yajl_parser_config cfg = { 1, 1 };
    hand = yajl_alloc(&callbacks, &cfg, NULL, (void *) &l);
#endif

    if (hand) {
        stat =
            yajl_parse(hand, (const unsigned char *) json.c_str(), json.size());
        yajl_free(hand);

        if (stat == yajl_status_ok) {
            return ParseElements(l);
        } else {
            throw ValueException("In-valid json");
        }
    }
    return Value();
}