Compile mysql_binlog and mysql_utils as C++
This commit is contained in:
657
server/core/mysql_binlog.cc
Normal file
657
server/core/mysql_binlog.cc
Normal file
@ -0,0 +1,657 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mysql_binlog.c - Extracting information from binary logs
|
||||
*/
|
||||
|
||||
#include <maxscale/mysql_binlog.h>
|
||||
#include <maxscale/mysql_utils.h>
|
||||
#include <stdlib.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <string.h>
|
||||
#include <maxscale/debug.h>
|
||||
#include <maxscale/users.h>
|
||||
#include <strings.h>
|
||||
#include <math.h>
|
||||
|
||||
/**
|
||||
* @brief Convert a table column type to a string
|
||||
*
|
||||
* @param type The table column type
|
||||
* @return The type of the column in human readable format
|
||||
* @see mxs_lestr_consume
|
||||
*/
|
||||
const char* column_type_to_string(uint8_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TABLE_COL_TYPE_DECIMAL:
|
||||
return "DECIMAL";
|
||||
case TABLE_COL_TYPE_TINY:
|
||||
return "TINY";
|
||||
case TABLE_COL_TYPE_SHORT:
|
||||
return "SHORT";
|
||||
case TABLE_COL_TYPE_LONG:
|
||||
return "LONG";
|
||||
case TABLE_COL_TYPE_FLOAT:
|
||||
return "FLOAT";
|
||||
case TABLE_COL_TYPE_DOUBLE:
|
||||
return "DOUBLE";
|
||||
case TABLE_COL_TYPE_NULL:
|
||||
return "NULL";
|
||||
case TABLE_COL_TYPE_TIMESTAMP:
|
||||
return "TIMESTAMP";
|
||||
case TABLE_COL_TYPE_LONGLONG:
|
||||
return "LONGLONG";
|
||||
case TABLE_COL_TYPE_INT24:
|
||||
return "INT24";
|
||||
case TABLE_COL_TYPE_DATE:
|
||||
return "DATE";
|
||||
case TABLE_COL_TYPE_TIME:
|
||||
return "TIME";
|
||||
case TABLE_COL_TYPE_DATETIME:
|
||||
return "DATETIME";
|
||||
case TABLE_COL_TYPE_YEAR:
|
||||
return "YEAR";
|
||||
case TABLE_COL_TYPE_NEWDATE:
|
||||
return "NEWDATE";
|
||||
case TABLE_COL_TYPE_VARCHAR:
|
||||
return "VARCHAR";
|
||||
case TABLE_COL_TYPE_BIT:
|
||||
return "BIT";
|
||||
case TABLE_COL_TYPE_TIMESTAMP2:
|
||||
return "TIMESTAMP2";
|
||||
case TABLE_COL_TYPE_DATETIME2:
|
||||
return "DATETIME2";
|
||||
case TABLE_COL_TYPE_TIME2:
|
||||
return "TIME2";
|
||||
case TABLE_COL_TYPE_NEWDECIMAL:
|
||||
return "NEWDECIMAL";
|
||||
case TABLE_COL_TYPE_ENUM:
|
||||
return "ENUM";
|
||||
case TABLE_COL_TYPE_SET:
|
||||
return "SET";
|
||||
case TABLE_COL_TYPE_TINY_BLOB:
|
||||
return "TINY_BLOB";
|
||||
case TABLE_COL_TYPE_MEDIUM_BLOB:
|
||||
return "MEDIUM_BLOB";
|
||||
case TABLE_COL_TYPE_LONG_BLOB:
|
||||
return "LONG_BLOB";
|
||||
case TABLE_COL_TYPE_BLOB:
|
||||
return "BLOB";
|
||||
case TABLE_COL_TYPE_VAR_STRING:
|
||||
return "VAR_STRING";
|
||||
case TABLE_COL_TYPE_STRING:
|
||||
return "STRING";
|
||||
case TABLE_COL_TYPE_GEOMETRY:
|
||||
return "GEOMETRY";
|
||||
default:
|
||||
ss_dassert(false);
|
||||
break;
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
bool column_is_blob(uint8_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TABLE_COL_TYPE_TINY_BLOB:
|
||||
case TABLE_COL_TYPE_MEDIUM_BLOB:
|
||||
case TABLE_COL_TYPE_LONG_BLOB:
|
||||
case TABLE_COL_TYPE_BLOB:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the column is a string type column
|
||||
*
|
||||
* @param type Type of the column
|
||||
* @return True if the column is a string type column
|
||||
* @see mxs_lestr_consume
|
||||
*/
|
||||
bool column_is_variable_string(uint8_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TABLE_COL_TYPE_DECIMAL:
|
||||
case TABLE_COL_TYPE_VARCHAR:
|
||||
case TABLE_COL_TYPE_BIT:
|
||||
case TABLE_COL_TYPE_NEWDECIMAL:
|
||||
case TABLE_COL_TYPE_VAR_STRING:
|
||||
case TABLE_COL_TYPE_GEOMETRY:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Detect BIT type columns
|
||||
* @param type Type of the column
|
||||
* @return True if the column is a BIT
|
||||
*/
|
||||
bool column_is_bit(uint8_t type)
|
||||
{
|
||||
return type == TABLE_COL_TYPE_BIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a column is of a temporal type
|
||||
* @param type Column type
|
||||
* @return True if the type is temporal
|
||||
*/
|
||||
bool column_is_temporal(uint8_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TABLE_COL_TYPE_YEAR:
|
||||
case TABLE_COL_TYPE_DATE:
|
||||
case TABLE_COL_TYPE_TIME:
|
||||
case TABLE_COL_TYPE_TIME2:
|
||||
case TABLE_COL_TYPE_DATETIME:
|
||||
case TABLE_COL_TYPE_DATETIME2:
|
||||
case TABLE_COL_TYPE_TIMESTAMP:
|
||||
case TABLE_COL_TYPE_TIMESTAMP2:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the column is a string type column
|
||||
*
|
||||
* @param type Type of the column
|
||||
* @return True if the column is a string type column
|
||||
* @see mxs_lestr_consume
|
||||
*/
|
||||
bool column_is_fixed_string(uint8_t type)
|
||||
{
|
||||
return type == TABLE_COL_TYPE_STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a column is a DECIMAL field
|
||||
* @param type Column type
|
||||
* @return True if column is DECIMAL
|
||||
*/
|
||||
bool column_is_decimal(uint8_t type)
|
||||
{
|
||||
return type == TABLE_COL_TYPE_NEWDECIMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a column is an ENUM or SET
|
||||
* @param type Column type
|
||||
* @return True if column is either ENUM or SET
|
||||
*/
|
||||
bool fixed_string_is_enum(uint8_t type)
|
||||
{
|
||||
return type == TABLE_COL_TYPE_ENUM || type == TABLE_COL_TYPE_SET;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpack a YEAR type
|
||||
*
|
||||
* The value seems to be stored as an offset from the year 1900
|
||||
* @param val Stored value
|
||||
* @param dest Destination where unpacked value is stored
|
||||
*/
|
||||
static void unpack_year(uint8_t *ptr, struct tm *dest)
|
||||
{
|
||||
memset(dest, 0, sizeof(*dest));
|
||||
dest->tm_year = *ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpack a DATETIME
|
||||
*
|
||||
* The DATETIME is stored as a 8 byte value with the values stored as multiples
|
||||
* of 100. This means that the stored value is in the format YYYYMMDDHHMMSS.
|
||||
* @param val Value read from the binary log
|
||||
* @param dest Pointer where the unpacked value is stored
|
||||
*/
|
||||
static void unpack_datetime(uint8_t *ptr, struct tm *dest)
|
||||
{
|
||||
uint64_t val = 0;
|
||||
memcpy(&val, ptr, sizeof(val));
|
||||
uint32_t second = val - ((val / 100) * 100);
|
||||
val /= 100;
|
||||
uint32_t minute = val - ((val / 100) * 100);
|
||||
val /= 100;
|
||||
uint32_t hour = val - ((val / 100) * 100);
|
||||
val /= 100;
|
||||
uint32_t day = val - ((val / 100) * 100);
|
||||
val /= 100;
|
||||
uint32_t month = val - ((val / 100) * 100);
|
||||
val /= 100;
|
||||
uint32_t year = val;
|
||||
|
||||
memset(dest, 0, sizeof(struct tm));
|
||||
dest->tm_year = year - 1900;
|
||||
dest->tm_mon = month - 1;
|
||||
dest->tm_mday = day;
|
||||
dest->tm_hour = hour;
|
||||
dest->tm_min = minute;
|
||||
dest->tm_sec = second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a 5 byte reverse byte order value
|
||||
* @param data pointer to data
|
||||
* @return Unpacked value
|
||||
*/
|
||||
static inline uint64_t unpack5(uint8_t* data)
|
||||
{
|
||||
uint64_t rval = data[4];
|
||||
rval += ((uint64_t)data[3]) << 8;
|
||||
rval += ((uint64_t)data[2]) << 16;
|
||||
rval += ((uint64_t)data[1]) << 24;
|
||||
rval += ((uint64_t)data[0]) << 32;
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** The DATETIME values are stored in the binary logs with an offset */
|
||||
#define DATETIME2_OFFSET 0x8000000000LL
|
||||
|
||||
/**
|
||||
* @brief Unpack a DATETIME2
|
||||
*
|
||||
* The DATETIME2 is only used by row based replication in newer MariaDB servers.
|
||||
* @param val Value read from the binary log
|
||||
* @param dest Pointer where the unpacked value is stored
|
||||
*/
|
||||
static void unpack_datetime2(uint8_t *ptr, uint8_t decimals, struct tm *dest)
|
||||
{
|
||||
int64_t unpacked = unpack5(ptr) - DATETIME2_OFFSET;
|
||||
if (unpacked < 0)
|
||||
{
|
||||
unpacked = -unpacked;
|
||||
}
|
||||
|
||||
uint64_t date = unpacked >> 17;
|
||||
uint64_t yearmonth = date >> 5;
|
||||
uint64_t time = unpacked % (1 << 17);
|
||||
|
||||
memset(dest, 0, sizeof(*dest));
|
||||
dest->tm_sec = time % (1 << 6);
|
||||
dest->tm_min = (time >> 6) % (1 << 6);
|
||||
dest->tm_hour = time >> 12;
|
||||
dest->tm_mday = date % (1 << 5);
|
||||
dest->tm_mon = (yearmonth % 13) - 1;
|
||||
|
||||
/** struct tm stores the year as: Year - 1900 */
|
||||
dest->tm_year = (yearmonth / 13) - 1900;
|
||||
}
|
||||
|
||||
/** Unpack a "reverse" byte order value */
|
||||
#define unpack4(data) (data[3] + (data[2] << 8) + (data[1] << 16) + (data[0] << 24))
|
||||
|
||||
/**
|
||||
* @brief Unpack a TIMESTAMP
|
||||
*
|
||||
* The timestamps are stored with the high bytes first
|
||||
* @param val The stored value
|
||||
* @param dest Destination where the result is stored
|
||||
*/
|
||||
static void unpack_timestamp(uint8_t *ptr, uint8_t decimals, struct tm *dest)
|
||||
{
|
||||
time_t t = unpack4(ptr);
|
||||
localtime_r(&t, dest);
|
||||
}
|
||||
|
||||
#define unpack3(data) (data[2] + (data[1] << 8) + (data[0] << 16))
|
||||
|
||||
/**
|
||||
* @brief Unpack a TIME
|
||||
*
|
||||
* The TIME is stored as a 3 byte value with the values stored as multiples
|
||||
* of 100. This means that the stored value is in the format HHMMSS.
|
||||
* @param val Value read from the binary log
|
||||
* @param dest Pointer where the unpacked value is stored
|
||||
*/
|
||||
static void unpack_time(uint8_t *ptr, struct tm *dest)
|
||||
{
|
||||
uint64_t val = unpack3(ptr);
|
||||
uint32_t second = val - ((val / 100) * 100);
|
||||
val /= 100;
|
||||
uint32_t minute = val - ((val / 100) * 100);
|
||||
val /= 100;
|
||||
uint32_t hour = val;
|
||||
|
||||
memset(dest, 0, sizeof(struct tm));
|
||||
dest->tm_hour = hour;
|
||||
dest->tm_min = minute;
|
||||
dest->tm_sec = second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpack a DATE value
|
||||
* @param ptr Pointer to packed value
|
||||
* @param dest Pointer where the unpacked value is stored
|
||||
*/
|
||||
static void unpack_date(uint8_t *ptr, struct tm *dest)
|
||||
{
|
||||
uint64_t val = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);
|
||||
memset(dest, 0, sizeof(struct tm));
|
||||
dest->tm_mday = val & 31;
|
||||
dest->tm_mon = ((val >> 5) & 15) - 1;
|
||||
dest->tm_year = (val >> 9) - 1900;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpack an ENUM or SET field
|
||||
* @param ptr Pointer to packed value
|
||||
* @param metadata Pointer to field metadata
|
||||
* @return Length of the processed field in bytes
|
||||
*/
|
||||
size_t unpack_enum(uint8_t *ptr, uint8_t *metadata, uint8_t *dest)
|
||||
{
|
||||
memcpy(dest, ptr, metadata[1]);
|
||||
return metadata[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpack a BIT
|
||||
*
|
||||
* A part of the BIT values are stored in the NULL value bitmask of the row event.
|
||||
* This makes extracting them a bit more complicated since the other fields
|
||||
* in the table could have an effect on the location of the stored values.
|
||||
*
|
||||
* It is possible that the BIT value is fully stored in the NULL value bitmask
|
||||
* which means that the actual row data is zero bytes for this field.
|
||||
* @param ptr Pointer to packed value
|
||||
* @param null_mask NULL field mask
|
||||
* @param col_count Number of columns in the row event
|
||||
* @param curr_col_index Current position of the field in the row event (zero indexed)
|
||||
* @param metadata Field metadata
|
||||
* @param dest Destination where the value is stored
|
||||
* @return Length of the processed field in bytes
|
||||
*/
|
||||
size_t unpack_bit(uint8_t *ptr, uint8_t *null_mask, uint32_t col_count,
|
||||
uint32_t curr_col_index, uint8_t *metadata, uint64_t *dest)
|
||||
{
|
||||
if (metadata[1])
|
||||
{
|
||||
memcpy(ptr, dest, metadata[1]);
|
||||
}
|
||||
return metadata[1];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the length of a temporal field
|
||||
* @param type Field type
|
||||
* @param decimals How many decimals the field has
|
||||
* @return Number of bytes the temporal value takes
|
||||
*/
|
||||
static size_t temporal_field_size(uint8_t type, uint8_t decimals)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TABLE_COL_TYPE_YEAR:
|
||||
return 1;
|
||||
|
||||
case TABLE_COL_TYPE_TIME:
|
||||
case TABLE_COL_TYPE_DATE:
|
||||
return 3;
|
||||
|
||||
case TABLE_COL_TYPE_TIME2:
|
||||
return 3 + ((decimals + 1) / 2);
|
||||
|
||||
case TABLE_COL_TYPE_DATETIME:
|
||||
return 8;
|
||||
|
||||
case TABLE_COL_TYPE_TIMESTAMP:
|
||||
return 4;
|
||||
|
||||
case TABLE_COL_TYPE_TIMESTAMP2:
|
||||
return 4 + ((decimals + 1) / 2);
|
||||
|
||||
case TABLE_COL_TYPE_DATETIME2:
|
||||
return 5 + ((decimals + 1) / 2);
|
||||
|
||||
default:
|
||||
MXS_ERROR("Unknown field type: %x %s", type, column_type_to_string(type));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpack a temporal value
|
||||
*
|
||||
* MariaDB and MySQL both store temporal values in a special format. This function
|
||||
* unpacks them from the storage format and into a common, usable format.
|
||||
* @param type Column type
|
||||
* @param val Extracted packed value
|
||||
* @param tm Pointer where the unpacked temporal value is stored
|
||||
*/
|
||||
size_t unpack_temporal_value(uint8_t type, uint8_t *ptr, uint8_t *metadata, struct tm *tm)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TABLE_COL_TYPE_YEAR:
|
||||
unpack_year(ptr, tm);
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_DATETIME:
|
||||
unpack_datetime(ptr, tm);
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_DATETIME2:
|
||||
unpack_datetime2(ptr, *metadata, tm);
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_TIME:
|
||||
unpack_time(ptr, tm);
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_DATE:
|
||||
unpack_date(ptr, tm);
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_TIMESTAMP:
|
||||
case TABLE_COL_TYPE_TIMESTAMP2:
|
||||
unpack_timestamp(ptr, *metadata, tm);
|
||||
break;
|
||||
|
||||
default:
|
||||
ss_dassert(false);
|
||||
break;
|
||||
}
|
||||
return temporal_field_size(type, *metadata);
|
||||
}
|
||||
|
||||
void format_temporal_value(char *str, size_t size, uint8_t type, struct tm *tm)
|
||||
{
|
||||
const char *format = "";
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TABLE_COL_TYPE_DATETIME:
|
||||
case TABLE_COL_TYPE_DATETIME2:
|
||||
case TABLE_COL_TYPE_TIMESTAMP:
|
||||
case TABLE_COL_TYPE_TIMESTAMP2:
|
||||
format = "%Y-%m-%d %H:%M:%S";
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_TIME:
|
||||
case TABLE_COL_TYPE_TIME2:
|
||||
format = "%H:%M:%S";
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_DATE:
|
||||
format = "%Y-%m-%d";
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_YEAR:
|
||||
format = "%Y";
|
||||
break;
|
||||
|
||||
default:
|
||||
MXS_ERROR("Unexpected temporal type: %x %s", type, column_type_to_string(type));
|
||||
ss_dassert(false);
|
||||
break;
|
||||
}
|
||||
strftime(str, size, format, tm);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract a value from a row event
|
||||
*
|
||||
* This function extracts a single value from a row event and stores it for
|
||||
* further processing. Integer values are usable immediately but temporal
|
||||
* values need to be unpacked from the compact format they are stored in.
|
||||
* @param ptr Pointer to the start of the field value
|
||||
* @param type Column type of the field
|
||||
* @param metadata Pointer to the field metadata
|
||||
* @param val Destination where the extracted value is stored
|
||||
* @return Number of bytes copied
|
||||
* @see extract_temporal_value
|
||||
*/
|
||||
size_t unpack_numeric_field(uint8_t *src, uint8_t type, uint8_t *metadata, uint8_t *dest)
|
||||
{
|
||||
size_t size = 0;
|
||||
switch (type)
|
||||
{
|
||||
case TABLE_COL_TYPE_LONG:
|
||||
case TABLE_COL_TYPE_FLOAT:
|
||||
size = 4;
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_INT24:
|
||||
size = 3;
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_LONGLONG:
|
||||
case TABLE_COL_TYPE_DOUBLE:
|
||||
size = 8;
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_SHORT:
|
||||
size = 2;
|
||||
break;
|
||||
|
||||
case TABLE_COL_TYPE_TINY:
|
||||
size = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
MXS_ERROR("Bad column type: %x %s", type, column_type_to_string(type));
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(dest, src, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
static uint64_t unpack_bytes(uint8_t *ptr, size_t bytes)
|
||||
{
|
||||
uint64_t val = 0;
|
||||
|
||||
switch (bytes)
|
||||
{
|
||||
case 1:
|
||||
val = ptr[0];
|
||||
break;
|
||||
case 2:
|
||||
val = ptr[1] | ((uint64_t)(ptr[0]) << 8);
|
||||
break;
|
||||
case 3:
|
||||
val = (uint64_t)ptr[2] | ((uint64_t)ptr[1] << 8) |
|
||||
((uint64_t)ptr[0] << 16);
|
||||
break;
|
||||
case 4:
|
||||
val = (uint64_t)ptr[3] | ((uint64_t)ptr[2] << 8) |
|
||||
((uint64_t)ptr[1] << 16) | ((uint64_t)ptr[0] << 24);
|
||||
break;
|
||||
case 5:
|
||||
val = (uint64_t)ptr[4] | ((uint64_t)ptr[3] << 8) |
|
||||
((uint64_t)ptr[2] << 16) | ((uint64_t)ptr[1] << 24) |
|
||||
((uint64_t)ptr[0] << 32);
|
||||
break;
|
||||
case 6:
|
||||
val = (uint64_t)ptr[5] | ((uint64_t)ptr[4] << 8) |
|
||||
((uint64_t)ptr[3] << 16) | ((uint64_t)ptr[2] << 24) |
|
||||
((uint64_t)ptr[1] << 32) | ((uint64_t)ptr[0] << 40);
|
||||
break;
|
||||
case 7:
|
||||
val = (uint64_t)ptr[6] | ((uint64_t)ptr[5] << 8) |
|
||||
((uint64_t)ptr[4] << 16) | ((uint64_t)ptr[3] << 24) |
|
||||
((uint64_t)ptr[2] << 32) | ((uint64_t)ptr[1] << 40) |
|
||||
((uint64_t)ptr[0] << 48);
|
||||
break;
|
||||
case 8:
|
||||
val = (uint64_t)ptr[7] | ((uint64_t)ptr[6] << 8) |
|
||||
((uint64_t)ptr[5] << 16) | ((uint64_t)ptr[4] << 24) |
|
||||
((uint64_t)ptr[3] << 32) | ((uint64_t)ptr[2] << 40) |
|
||||
((uint64_t)ptr[1] << 48) | ((uint64_t)ptr[0] << 56);
|
||||
break;
|
||||
|
||||
default:
|
||||
ss_dassert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
size_t unpack_decimal_field(uint8_t *ptr, uint8_t *metadata, double *val_float)
|
||||
{
|
||||
const int dec_dig = 9;
|
||||
int precision = metadata[0];
|
||||
int decimals = metadata[1];
|
||||
int dig_bytes[] = {0, 1, 1, 2, 2, 3, 3, 4, 4, 4};
|
||||
int ipart = precision - decimals;
|
||||
int ipart1 = ipart / dec_dig;
|
||||
int fpart1 = decimals / dec_dig;
|
||||
int ipart2 = ipart - ipart1 * dec_dig;
|
||||
int fpart2 = decimals - fpart1 * dec_dig;
|
||||
int ibytes = ipart1 * 4 + dig_bytes[ipart2];
|
||||
int fbytes = fpart1 * 4 + dig_bytes[fpart2];
|
||||
|
||||
/** Remove the sign bit and store it locally */
|
||||
bool negative = (ptr[0] & 0x80) == 0;
|
||||
ptr[0] ^= 0x80;
|
||||
|
||||
if (negative)
|
||||
{
|
||||
for (int i = 0; i < ibytes; i++)
|
||||
{
|
||||
ptr[i] = ~ptr[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < fbytes; i++)
|
||||
{
|
||||
ptr[i + ibytes] = ~ptr[i + ibytes];
|
||||
}
|
||||
}
|
||||
|
||||
int64_t val_i = unpack_bytes(ptr, ibytes);
|
||||
int64_t val_f = fbytes ? unpack_bytes(ptr + ibytes, fbytes) : 0;
|
||||
|
||||
if (negative)
|
||||
{
|
||||
val_i = -val_i;
|
||||
val_f = -val_f;
|
||||
}
|
||||
|
||||
*val_float = (double)val_i + ((double)val_f / (pow(10.0, decimals)));
|
||||
|
||||
return ibytes + fbytes;
|
||||
}
|
||||
Reference in New Issue
Block a user