/**
@file SiPropertyWriter.hpp
@brief Properties Writer for SI
@details Copyright (c) 2024 Acronis International GmbH
@author Denis Kopyrin (Denis.Kopyrin@acronis.com)
@since $Id: $
*/
#pragma once
#include "debug.h"
#include "file_handle_tools.h"
#include "si_common.h"
#include "si_size.h"
#include <linux/compiler.h>
#include <linux/string.h>
typedef struct
{
uint8_t* buffer;
uint8_t* properties;
uint8_t* curr;
uint32_t max_size;
int properties_written;
} si_property_writer_t;
static inline void si_property_writer_init(si_property_writer_t* writer, SiProperty* buffer, uint32_t size)
{
writer->max_size = size;
writer->buffer = (uint8_t*) buffer;
writer->properties = writer->buffer;
writer->curr = writer->properties;
writer->properties_written = 0;
}
static inline uint32_t si_property_writer_size_to_data_end(const si_property_writer_t* writer, uint8_t* ptr)
{
return (uint32_t) (writer->curr - ptr);
}
static inline uint8_t* si_property_writer_get_data(const si_property_writer_t* writer)
{
return writer->buffer;
}
static inline uint32_t si_property_writer_get_size(const si_property_writer_t* writer)
{
return si_property_writer_size_to_data_end(writer, writer->buffer);
}
// Peeks buffer for given property id and returns buffer where value of 'size' can be written to.
// Commit the change with 'si_property_writer_commit' when data will be written.
// If 'si_property_writer_commit' is not going to be called, next 'peek' call will overwrite the previous one.
// 'size' parameter is only used for validation purposes.
static inline void* si_property_writer_peek(const si_property_writer_t* writer, fp_si_property_id_t prop_id, uint32_t size)
{
fp_si_property_value_type_t value_type = fp_si_property_id_to_fp_value_type(prop_id);
SiPropertyValueType value_type_pub = fp_si_property_value_type_to_public_type(value_type);
bool wants_vector_wrap = si_property_value_type_wants_vector_wrap(value_type_pub);
uint32_t total_size;
uint32_t curr_size;
SiProperty* prop;
void* data;
total_size = size + sizeof(SiProperty) + (wants_vector_wrap ? sizeof(SiVector) : 0);
curr_size = si_property_writer_get_size(writer);
#ifdef KERNEL_MOCK
BUG_ON(curr_size + total_size > writer->max_size);
#endif
if (unlikely(curr_size + total_size > writer->max_size))
{
WPRINTF_RATELIMITED("Buffer too small for prop_id=%d: curr_size=%u total_size=%u max_size=%u", prop_id, curr_size, total_size, writer->max_size);
return NULL;
}
prop = (SiProperty*) writer->curr;
// Varsized fields are presented as SiVector
if (!wants_vector_wrap)
{
data = prop->ValueBuffer;
}
else
{
SiVector* vector = (SiVector*) prop->ValueBuffer;
data = vector->VectorBuffer;
}
return data;
}
// Commits the previously allocated buffer with 'si_property_write_peek'.
static inline void si_property_writer_commit(si_property_writer_t* writer, fp_si_property_id_t prop_id, uint32_t size)
{
SiProperty* prop;
fp_si_property_value_type_t value_type = fp_si_property_id_to_fp_value_type(prop_id);
SiPropertyValueType value_type_pub = fp_si_property_value_type_to_public_type(value_type);
bool wants_vector_wrap = si_property_value_type_wants_vector_wrap(value_type_pub);
uint32_t total_size;
uint32_t curr_size;
total_size = size + sizeof(SiProperty) + (wants_vector_wrap ? sizeof(SiVector) : 0);
curr_size = si_property_writer_get_size(writer);
#ifdef KERNEL_MOCK
BUG_ON(curr_size + total_size > writer->max_size);
#endif
if (unlikely(curr_size + total_size > writer->max_size))
{
WPRINTF_RATELIMITED("Buffer too small for prop_id=%d: curr_size=%u total_size=%u max_size=%u", prop_id, curr_size, total_size, writer->max_size);
return;
}
// Fill in SiProperty
prop = (SiProperty*) writer->curr;
prop->Size = total_size;
prop->PropertyId = fp_si_property_id_to_public_type(prop_id);
prop->ValueType = value_type_pub;
// Adjust size of SiVector
if (wants_vector_wrap)
{
SiVector* vector = (SiVector*) prop->ValueBuffer;
vector->SizeInBytes = size;
}
writer->curr += total_size;
writer->properties_written++;
}
static inline void si_property_writer_write(si_property_writer_t* writer, fp_si_property_id_t prop_id, const void* data, uint32_t size)
{
void* buf = si_property_writer_peek(writer, prop_id, size);
if (unlikely(!buf))
return;
if (likely(0 != size))
__builtin_memcpy(buf, data, size);
si_property_writer_commit(writer, prop_id, size);
}
static inline void si_property_writer_write2(si_property_writer_t* writer, fp_si_property_id_t prop_id, const void* data1, uint32_t size1, const void* data2, uint32_t size2)
{
uint32_t size = size1 + size2;
uint8_t* buf = (uint8_t*) si_property_writer_peek(writer, prop_id, size);
if (unlikely(!buf))
return;
if (unlikely(0 != size1))
__builtin_memcpy(buf , data1, size1);
if (unlikely(0 != size2))
__builtin_memcpy(buf + size1, data2, size2);
si_property_writer_commit(writer, prop_id, size);
}
#define SI_FP_PROP(propId, propIdPub, valueType, type, name) \
static inline void si_property_writer_write_##name(si_property_writer_t* writer, type val) \
{ return si_property_writer_write(writer, propId, &val, sizeof(val)); }
#define SI_FP_PROP_SIZED(propId, propIdPub, valueType, type, name) \
static inline void si_property_writer_write_##name(si_property_writer_t* writer, type val) \
{ return si_property_writer_write(writer, propId, val.value, val.length); }
#define SI_FP_PROP_HANDLE(propId, propIdPub, valueType, type, name) \
static inline void si_property_writer_write_##name(si_property_writer_t* writer, type val) \
{ return si_property_writer_write2(writer, propId, &val->handle_type, sizeof(val->handle_type), val->f_handle, val->handle_bytes); }
#include "si_fp_properties_x.h"
#undef SI_FP_PROP
#undef SI_FP_PROP_SIZED
#undef SI_FP_PROP_HANDLE
static inline void si_event_writer_init(si_property_writer_t* writer, SiEvent* buffer, uint32_t size)
{
si_property_writer_init(writer, buffer->FirstProperty, size);
}
static inline void si_event_writer_finalize(SiEvent* event
, const si_property_writer_t* writer)
{
event->Size = si_property_writer_size_to_data_end(writer, (uint8_t*) event);
event->PropertiesNumber = writer->properties_written;
}
static inline void si_info_writer_init(si_property_writer_t* writer, SiInfo* buffer, uint32_t size)
{
si_property_writer_init(writer, buffer->FirstProperty, size);
}
static inline void si_info_writer_finalize(SiInfo* info
, const si_property_writer_t* writer)
{
info->Size = si_property_writer_size_to_data_end(writer, (uint8_t*) info);
info->PropertiesNumber = writer->properties_written;
}
|