blob: 18fb3af3bc08784a8254586c91dba0c5820d4a35 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 <errno.h>
#include <stdlib.h>
#include <string.h>
#include "avro/allocation.h"
#include "avro/data.h"
#include "avro/errors.h"
#include "avro/value.h"
#include "avro_private.h"
#define check_return(retval, call) \
do { \
int rval = call; \
if (rval != 0) { return (retval); } \
} while (0)
void
avro_value_incref(avro_value_t *value)
{
value->iface->incref(value);
}
void
avro_value_decref(avro_value_t *value)
{
value->iface->decref(value);
avro_value_iface_decref(value->iface);
value->iface = NULL;
value->self = NULL;
}
void
avro_value_copy_ref(avro_value_t *dest, const avro_value_t *src)
{
dest->iface = src->iface;
dest->self = src->self;
avro_value_iface_incref(dest->iface);
dest->iface->incref(dest);
}
void
avro_value_move_ref(avro_value_t *dest, avro_value_t *src)
{
dest->iface = src->iface;
dest->self = src->self;
src->iface = NULL;
src->self = NULL;
}
int
avro_value_equal_fast(avro_value_t *val1, avro_value_t *val2)
{
avro_type_t type1 = avro_value_get_type(val1);
avro_type_t type2 = avro_value_get_type(val2);
if (type1 != type2) {
return 0;
}
switch (type1) {
case AVRO_BOOLEAN:
{
int v1;
int v2;
check_return(0, avro_value_get_boolean(val1, &v1));
check_return(0, avro_value_get_boolean(val2, &v2));
return (v1 == v2);
}
case AVRO_BYTES:
{
const void *buf1;
const void *buf2;
size_t size1;
size_t size2;
check_return(0, avro_value_get_bytes(val1, &buf1, &size1));
check_return(0, avro_value_get_bytes(val2, &buf2, &size2));
if (size1 != size2) {
return 0;
}
return (memcmp(buf1, buf2, size1) == 0);
}
case AVRO_DOUBLE:
{
double v1;
double v2;
check_return(0, avro_value_get_double(val1, &v1));
check_return(0, avro_value_get_double(val2, &v2));
return (v1 == v2);
}
case AVRO_FLOAT:
{
float v1;
float v2;
check_return(0, avro_value_get_float(val1, &v1));
check_return(0, avro_value_get_float(val2, &v2));
return (v1 == v2);
}
case AVRO_INT32:
{
int32_t v1;
int32_t v2;
check_return(0, avro_value_get_int(val1, &v1));
check_return(0, avro_value_get_int(val2, &v2));
return (v1 == v2);
}
case AVRO_INT64:
{
int64_t v1;
int64_t v2;
check_return(0, avro_value_get_long(val1, &v1));
check_return(0, avro_value_get_long(val2, &v2));
return (v1 == v2);
}
case AVRO_NULL:
{
check_return(0, avro_value_get_null(val1));
check_return(0, avro_value_get_null(val2));
return 1;
}
case AVRO_STRING:
{
const char *buf1;
const char *buf2;
size_t size1;
size_t size2;
check_return(0, avro_value_get_string(val1, &buf1, &size1));
check_return(0, avro_value_get_string(val2, &buf2, &size2));
if (size1 != size2) {
return 0;
}
return (memcmp(buf1, buf2, size1) == 0);
}
case AVRO_ARRAY:
{
size_t count1;
size_t count2;
check_return(0, avro_value_get_size(val1, &count1));
check_return(0, avro_value_get_size(val2, &count2));
if (count1 != count2) {
return 0;
}
size_t i;
for (i = 0; i < count1; i++) {
avro_value_t child1;
avro_value_t child2;
check_return(0, avro_value_get_by_index
(val1, i, &child1, NULL));
check_return(0, avro_value_get_by_index
(val2, i, &child2, NULL));
if (!avro_value_equal_fast(&child1, &child2)) {
return 0;
}
}
return 1;
}
case AVRO_ENUM:
{
int v1;
int v2;
check_return(0, avro_value_get_enum(val1, &v1));
check_return(0, avro_value_get_enum(val2, &v2));
return (v1 == v2);
}
case AVRO_FIXED:
{
const void *buf1;
const void *buf2;
size_t size1;
size_t size2;
check_return(0, avro_value_get_fixed(val1, &buf1, &size1));
check_return(0, avro_value_get_fixed(val2, &buf2, &size2));
if (size1 != size2) {
return 0;
}
return (memcmp(buf1, buf2, size1) == 0);
}
case AVRO_MAP:
{
size_t count1;
size_t count2;
check_return(0, avro_value_get_size(val1, &count1));
check_return(0, avro_value_get_size(val2, &count2));
if (count1 != count2) {
return 0;
}
size_t i;
for (i = 0; i < count1; i++) {
avro_value_t child1;
avro_value_t child2;
const char *key1;
check_return(0, avro_value_get_by_index
(val1, i, &child1, &key1));
check_return(0, avro_value_get_by_name
(val2, key1, &child2, NULL));
if (!avro_value_equal_fast(&child1, &child2)) {
return 0;
}
}
return 1;
}
case AVRO_RECORD:
{
size_t count1;
check_return(0, avro_value_get_size(val1, &count1));
size_t i;
for (i = 0; i < count1; i++) {
avro_value_t child1;
avro_value_t child2;
check_return(0, avro_value_get_by_index
(val1, i, &child1, NULL));
check_return(0, avro_value_get_by_index
(val2, i, &child2, NULL));
if (!avro_value_equal_fast(&child1, &child2)) {
return 0;
}
}
return 1;
}
case AVRO_UNION:
{
int disc1;
int disc2;
check_return(0, avro_value_get_discriminant(val1, &disc1));
check_return(0, avro_value_get_discriminant(val2, &disc2));
if (disc1 != disc2) {
return 0;
}
avro_value_t branch1;
avro_value_t branch2;
check_return(0, avro_value_get_current_branch(val1, &branch1));
check_return(0, avro_value_get_current_branch(val2, &branch2));
return avro_value_equal_fast(&branch1, &branch2);
}
default:
return 0;
}
}
int
avro_value_equal(avro_value_t *val1, avro_value_t *val2)
{
avro_schema_t schema1 = avro_value_get_schema(val1);
avro_schema_t schema2 = avro_value_get_schema(val2);
if (!avro_schema_equal(schema1, schema2)) {
return 0;
}
return avro_value_equal_fast(val1, val2);
}
#define cmp(v1, v2) \
(((v1) == (v2))? 0: \
((v1) < (v2))? -1: 1)
int
avro_value_cmp_fast(avro_value_t *val1, avro_value_t *val2)
{
avro_type_t type1 = avro_value_get_type(val1);
avro_type_t type2 = avro_value_get_type(val2);
if (type1 != type2) {
return -1;
}
switch (type1) {
case AVRO_BOOLEAN:
{
int v1;
int v2;
check_return(0, avro_value_get_boolean(val1, &v1));
check_return(0, avro_value_get_boolean(val2, &v2));
return cmp(!!v1, !!v2);
}
case AVRO_BYTES:
{
const void *buf1;
const void *buf2;
size_t size1;
size_t size2;
size_t min_size;
int result;
check_return(0, avro_value_get_bytes(val1, &buf1, &size1));
check_return(0, avro_value_get_bytes(val2, &buf2, &size2));
min_size = (size1 < size2)? size1: size2;
result = memcmp(buf1, buf2, min_size);
if (result != 0) {
return result;
} else {
return cmp(size1, size2);
}
}
case AVRO_DOUBLE:
{
double v1;
double v2;
check_return(0, avro_value_get_double(val1, &v1));
check_return(0, avro_value_get_double(val2, &v2));
return cmp(v1, v2);
}
case AVRO_FLOAT:
{
float v1;
float v2;
check_return(0, avro_value_get_float(val1, &v1));
check_return(0, avro_value_get_float(val2, &v2));
return cmp(v1, v2);
}
case AVRO_INT32:
{
int32_t v1;
int32_t v2;
check_return(0, avro_value_get_int(val1, &v1));
check_return(0, avro_value_get_int(val2, &v2));
return cmp(v1, v2);
}
case AVRO_INT64:
{
int64_t v1;
int64_t v2;
check_return(0, avro_value_get_long(val1, &v1));
check_return(0, avro_value_get_long(val2, &v2));
return cmp(v1, v2);
}
case AVRO_NULL:
{
check_return(0, avro_value_get_null(val1));
check_return(0, avro_value_get_null(val2));
return 0;
}
case AVRO_STRING:
{
const char *buf1;
const char *buf2;
size_t size1;
size_t size2;
size_t min_size;
int result;
check_return(0, avro_value_get_string(val1, &buf1, &size1));
check_return(0, avro_value_get_string(val2, &buf2, &size2));
min_size = (size1 < size2)? size1: size2;
result = memcmp(buf1, buf2, min_size);
if (result != 0) {
return result;
} else {
return cmp(size1, size2);
}
}
case AVRO_ARRAY:
{
size_t count1;
size_t count2;
size_t min_count;
size_t i;
check_return(0, avro_value_get_size(val1, &count1));
check_return(0, avro_value_get_size(val2, &count2));
min_count = (count1 < count2)? count1: count2;
for (i = 0; i < min_count; i++) {
avro_value_t child1;
avro_value_t child2;
int result;
check_return(0, avro_value_get_by_index
(val1, i, &child1, NULL));
check_return(0, avro_value_get_by_index
(val2, i, &child2, NULL));
result = avro_value_cmp_fast(&child1, &child2);
if (result != 0) {
return result;
}
}
return cmp(count1, count2);
}
case AVRO_ENUM:
{
int v1;
int v2;
check_return(0, avro_value_get_enum(val1, &v1));
check_return(0, avro_value_get_enum(val2, &v2));
return cmp(v1, v2);
}
case AVRO_FIXED:
{
const void *buf1;
const void *buf2;
size_t size1;
size_t size2;
check_return(0, avro_value_get_fixed(val1, &buf1, &size1));
check_return(0, avro_value_get_fixed(val2, &buf2, &size2));
if (size1 != size2) {
return -1;
}
return memcmp(buf1, buf2, size1);
}
case AVRO_MAP:
{
return -1;
}
case AVRO_RECORD:
{
size_t count1;
check_return(0, avro_value_get_size(val1, &count1));
size_t i;
for (i = 0; i < count1; i++) {
avro_value_t child1;
avro_value_t child2;
int result;
check_return(0, avro_value_get_by_index
(val1, i, &child1, NULL));
check_return(0, avro_value_get_by_index
(val2, i, &child2, NULL));
result = avro_value_cmp_fast(&child1, &child2);
if (result != 0) {
return result;
}
}
return 0;
}
case AVRO_UNION:
{
int disc1;
int disc2;
check_return(0, avro_value_get_discriminant(val1, &disc1));
check_return(0, avro_value_get_discriminant(val2, &disc2));
if (disc1 == disc2) {
avro_value_t branch1;
avro_value_t branch2;
check_return(0, avro_value_get_current_branch(val1, &branch1));
check_return(0, avro_value_get_current_branch(val2, &branch2));
return avro_value_cmp_fast(&branch1, &branch2);
} else {
return cmp(disc1, disc2);
}
}
default:
return 0;
}
}
int
avro_value_cmp(avro_value_t *val1, avro_value_t *val2)
{
avro_schema_t schema1 = avro_value_get_schema(val1);
avro_schema_t schema2 = avro_value_get_schema(val2);
if (!avro_schema_equal(schema1, schema2)) {
return 0;
}
return avro_value_cmp_fast(val1, val2);
}
int
avro_value_copy_fast(avro_value_t *dest, const avro_value_t *src)
{
avro_type_t dest_type = avro_value_get_type(dest);
avro_type_t src_type = avro_value_get_type(src);
if (dest_type != src_type) {
return 0;
}
int rval;
check(rval, avro_value_reset(dest));
switch (dest_type) {
case AVRO_BOOLEAN:
{
int val;
check(rval, avro_value_get_boolean(src, &val));
return avro_value_set_boolean(dest, val);
}
case AVRO_BYTES:
{
avro_wrapped_buffer_t val;
check(rval, avro_value_grab_bytes(src, &val));
return avro_value_give_bytes(dest, &val);
}
case AVRO_DOUBLE:
{
double val;
check(rval, avro_value_get_double(src, &val));
return avro_value_set_double(dest, val);
}
case AVRO_FLOAT:
{
float val;
check(rval, avro_value_get_float(src, &val));
return avro_value_set_float(dest, val);
}
case AVRO_INT32:
{
int32_t val;
check(rval, avro_value_get_int(src, &val));
return avro_value_set_int(dest, val);
}
case AVRO_INT64:
{
int64_t val;
check(rval, avro_value_get_long(src, &val));
return avro_value_set_long(dest, val);
}
case AVRO_NULL:
{
check(rval, avro_value_get_null(src));
return avro_value_set_null(dest);
}
case AVRO_STRING:
{
avro_wrapped_buffer_t val;
check(rval, avro_value_grab_string(src, &val));
return avro_value_give_string_len(dest, &val);
}
case AVRO_ARRAY:
{
size_t count;
check(rval, avro_value_get_size(src, &count));
size_t i;
for (i = 0; i < count; i++) {
avro_value_t src_child;
avro_value_t dest_child;
check(rval, avro_value_get_by_index
(src, i, &src_child, NULL));
check(rval, avro_value_append
(dest, &dest_child, NULL));
check(rval, avro_value_copy_fast
(&dest_child, &src_child));
}
return 0;
}
case AVRO_ENUM:
{
int val;
check(rval, avro_value_get_enum(src, &val));
return avro_value_set_enum(dest, val);
}
case AVRO_FIXED:
{
avro_wrapped_buffer_t val;
check(rval, avro_value_grab_fixed(src, &val));
return avro_value_give_fixed(dest, &val);
}
case AVRO_MAP:
{
size_t count;
check(rval, avro_value_get_size(src, &count));
size_t i;
for (i = 0; i < count; i++) {
avro_value_t src_child;
avro_value_t dest_child;
const char *key;
check(rval, avro_value_get_by_index
(src, i, &src_child, &key));
check(rval, avro_value_add
(dest, key, &dest_child, NULL, NULL));
check(rval, avro_value_copy_fast
(&dest_child, &src_child));
}
return 0;
}
case AVRO_RECORD:
{
size_t count;
check(rval, avro_value_get_size(src, &count));
size_t i;
for (i = 0; i < count; i++) {
avro_value_t src_child;
avro_value_t dest_child;
check(rval, avro_value_get_by_index
(src, i, &src_child, NULL));
check(rval, avro_value_get_by_index
(dest, i, &dest_child, NULL));
check(rval, avro_value_copy_fast
(&dest_child, &src_child));
}
return 0;
}
case AVRO_UNION:
{
int disc;
check(rval, avro_value_get_discriminant(src, &disc));
avro_value_t src_branch;
avro_value_t dest_branch;
check(rval, avro_value_get_current_branch(src, &src_branch));
check(rval, avro_value_set_branch(dest, disc, &dest_branch));
return avro_value_copy_fast(&dest_branch, &src_branch);
}
default:
return 0;
}
}
int
avro_value_copy(avro_value_t *dest, const avro_value_t *src)
{
avro_schema_t dest_schema = avro_value_get_schema(dest);
avro_schema_t src_schema = avro_value_get_schema(src);
if (!avro_schema_equal(dest_schema, src_schema)) {
avro_set_error("Schemas don't match");
return EINVAL;
}
return avro_value_copy_fast(dest, src);
}