
The float and double types were calculated to exceed the internal buffer sizes even though the buffer was of the correct size.
397 lines
9.1 KiB
C
397 lines
9.1 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: 2020-01-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;
|
|
}
|
|
else
|
|
{
|
|
ss_dassert(!true);
|
|
MXS_ERROR("Block cannot hold a value of type float");
|
|
}
|
|
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
ss_dassert(!true);
|
|
MXS_ERROR("Block cannot hold a value of type double");
|
|
}
|
|
|
|
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;
|
|
}
|