Files
MaxScale/avro/maxavro.c
Markus Mäkelä b0bec7b9b8 Always read Avro headers from file
The headers were read from memory as if they were in compressed
format. This mistake was caused by the fact that the function names were
changed.
2017-04-20 14:22:55 +03:00

387 lines
8.9 KiB
C

/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "maxavro_internal.h"
#include <maxscale/log_manager.h>
#include <errno.h>
/** Maximum byte size of an integer value */
#define MAX_INTEGER_SIZE 10
#define avro_decode(n) ((n >> 1) ^ -(n & 1))
#define encode_long(n) ((n << 1) ^ (n >> 63))
#define more_bytes(b) (b & 0x80)
/**
* @brief Read an Avro integer
*
* The integer lengths are all variable and the last bit in a byte indicates
* if more bytes belong to the integer value. The real value of the integer is
* the concatenation of the lowest seven bits of each byte. This value is encoded
* in a zigzag patten i.e. first value is -1, second 1, third -2 and so on.
* @param file The source FILE handle
* @param dest Destination where the read value is written
* @return True if value was read successfully
*/
bool maxavro_read_integer(MAXAVRO_FILE* file, uint64_t *dest)
{
uint64_t rval = 0;
uint8_t nread = 0;
uint8_t byte;
do
{
if (nread >= MAX_INTEGER_SIZE)
{
file->last_error = MAXAVRO_ERR_VALUE_OVERFLOW;
return false;
}
if (file->buffer_ptr < file->buffer_end)
{
byte = *file->buffer_ptr;
file->buffer_ptr++;
}
else
{
return false;
}
rval |= (uint64_t)(byte & 0x7f) << (nread++ * 7);
}
while (more_bytes(byte));
if (dest)
{
*dest = avro_decode(rval);
}
return true;
}
bool maxavro_read_integer_from_file(MAXAVRO_FILE* file, uint64_t *dest)
{
uint64_t rval = 0;
uint8_t nread = 0;
uint8_t byte;
do
{
if (nread >= MAX_INTEGER_SIZE)
{
file->last_error = MAXAVRO_ERR_VALUE_OVERFLOW;
return false;
}
size_t rdsz = fread(&byte, sizeof(byte), 1, file->file);
if (rdsz != sizeof(byte))
{
if (rdsz != 0)
{
MXS_ERROR("Failed to read %lu bytes from '%s'", sizeof(byte), file->filename);
file->last_error = MAXAVRO_ERR_IO;
}
else
{
MXS_DEBUG("Read 0 bytes from file '%s'", file->filename);
}
return false;
}
rval |= (uint64_t)(byte & 0x7f) << (nread++ * 7);
}
while (more_bytes(byte));
if (dest)
{
*dest = avro_decode(rval);
}
return true;
}
/**
* @brief Calculate the length of an Avro integer
*
* @param val Vale to calculate
* @return Length of the value in bytes
*/
uint64_t avro_length_integer(uint64_t val)
{
uint64_t encval = encode_long(val);
uint8_t nbytes = 0;
while (more_bytes(encval))
{
nbytes++;
encval >>= 7;
}
return nbytes;
}
/**
* @brief Read an Avro string
*
* The strings are encoded as one Avro integer followed by that many bytes of
* data.
* @param file File to read from
* @return Pointer to newly allocated string or NULL if an error occurred
*
* @see maxavro_get_error
*/
char* maxavro_read_string(MAXAVRO_FILE* file, size_t* size)
{
char *key = NULL;
uint64_t len;
if (maxavro_read_integer(file, &len))
{
key = MXS_MALLOC(len + 1);
if (key)
{
memcpy(key, file->buffer_ptr, len);
key[len] = '\0';
file->buffer_ptr += len;
*size = len;
}
else
{
file->last_error = MAXAVRO_ERR_MEMORY;
}
}
return key;
}
/**
* @brief Read an Avro string
*
* The strings are encoded as one Avro integer followed by that many bytes of
* data.
* @param file File to read from
* @return Pointer to newly allocated string or NULL if an error occurred
*
* @see maxavro_get_error
*/
char* maxavro_read_string_from_file(MAXAVRO_FILE* file, size_t* size)
{
char *key = NULL;
uint64_t len;
if (maxavro_read_integer_from_file(file, &len))
{
key = MXS_MALLOC(len + 1);
if (key)
{
if (fread(key, 1, len, file->file) == len)
{
key[len] = '\0';
*size = len;
}
else
{
file->last_error = MAXAVRO_ERR_IO;
MXS_FREE(key);
key = NULL;
}
}
else
{
file->last_error = MAXAVRO_ERR_MEMORY;
}
}
return key;
}
/**
* @bref Skip an Avro string
*
* @param file Avro file handle
* @return True if the string was skipped, false if an error occurred.
* @see maxavro_get_error
*/
bool maxavro_skip_string(MAXAVRO_FILE* file)
{
uint64_t len;
if (maxavro_read_integer(file, &len))
{
file->buffer_ptr += len;
return true;
}
return false;
}
/**
* @brief Calculate the length of an Avro string
* @param val Vale to calculate
* @return Length of the string in bytes
*/
uint64_t avro_length_string(const char* str)
{
uint64_t slen = strlen(str);
uint64_t ilen = avro_length_integer(slen);
return slen + ilen;
}
/**
* @brief Read an Avro float
*
* The float is encoded as a 4 byte floating point value
* @param file File to read from
* @param dest Destination where the read value is stored
* @return True if value was read successfully, false if an error occurred
*
* @see maxavro_get_error
*/
bool maxavro_read_float(MAXAVRO_FILE* file, float *dest)
{
bool rval = false;
if (file->buffer_ptr + sizeof(*dest) < file->buffer_end)
{
memcpy(dest, file->buffer_ptr, sizeof(*dest));
file->buffer_ptr += sizeof(*dest);
rval = true;
}
return rval;
}
/**
* @brief Calculate the length of a float value
* @param val Vale to calculate
* @return Length of the value in bytes
*/
uint64_t avro_length_float(float val)
{
return sizeof(val);
}
/**
* @brief Read an Avro double
*
* The float is encoded as a 8 byte floating point value
* @param file File to read from
* @param dest Destination where the read value is stored
* @return True if value was read successfully, false if an error occurred
*
* @see maxavro_get_error
*/
bool maxavro_read_double(MAXAVRO_FILE* file, double *dest)
{
bool rval = false;
if (file->buffer_ptr + sizeof(*dest) < file->buffer_end)
{
memcpy(dest, file->buffer_ptr, sizeof(*dest));
file->buffer_ptr += sizeof(*dest);
rval = true;
}
return rval;
}
/**
* @brief Calculate the length of a double value
* @param val Vale to calculate
* @return Length of the value in bytes
*/
uint64_t avro_length_double(double val)
{
return sizeof(val);
}
/**
* @brief Read an Avro map
*
* A map is encoded as a series of blocks. Each block is encoded as an Avro
* integer followed by that many key-value pairs of Avro strings. The last
* block in the map will be a zero length block signaling its end.
* @param file File to read from
* @return A read map or NULL if an error occurred. The return value needs to be
* freed with maxavro_map_free().
*/
MAXAVRO_MAP* maxavro_read_map_from_file(MAXAVRO_FILE *file)
{
MAXAVRO_MAP* rval = NULL;
uint64_t blocks;
if (!maxavro_read_integer_from_file(file, &blocks))
{
return NULL;
}
while (blocks > 0)
{
for (long i = 0; i < blocks; i++)
{
size_t size;
MAXAVRO_MAP* val = calloc(1, sizeof(MAXAVRO_MAP));
if (val && (val->key = maxavro_read_string_from_file(file, &size)) && (val->value = maxavro_read_string_from_file(file, &size)))
{
val->next = rval;
rval = val;
}
else
{
maxavro_map_free(val);
maxavro_map_free(rval);
return NULL;
}
}
if (!maxavro_read_integer_from_file(file, &blocks))
{
maxavro_map_free(rval);
return NULL;
}
}
return rval;
}
/**
* @brief Free an Avro map
*
* @param value Map to free
*/
void maxavro_map_free(MAXAVRO_MAP *value)
{
while (value)
{
MAXAVRO_MAP* tmp = value;
value = value->next;
MXS_FREE(tmp->key);
MXS_FREE(tmp->value);
MXS_FREE(tmp);
}
}
/**
* @brief Calculate the length of an Avro map
* @param map Map to measure
* @return Length of the map in bytes
*/
uint64_t avro_map_length(MAXAVRO_MAP* map)
{
uint64_t len = avro_length_integer(map->blocks);
while (map)
{
len += avro_length_string(map->key);
len += avro_length_string(map->value);
map = map->next;
}
len += avro_length_integer(0);
return len;
}