Add octet string buffer duplication functions and refactor related structures (#1266)

* Add new functions for duplicating and copying octet string buffers.

* Move and update octet string value object structure with description string and add object-name and description WriteProperty handling using strdup. Add functional tests to ensure the correctness of the new features.
This commit is contained in:
Steve Karg
2026-03-18 13:13:00 -05:00
committed by GitHub
parent 1c9af1632b
commit 91090d3ff8
8 changed files with 525 additions and 74 deletions
-13
View File
@@ -88,19 +88,6 @@ typedef struct BACnetTag {
uint32_t len_value_type;
} BACNET_TAG;
typedef struct BACnetCharacterStringBuffer {
uint8_t encoding;
char *buffer;
size_t buffer_size;
uint32_t buffer_length;
} BACNET_CHARACTER_STRING_BUFFER;
typedef struct BACnetOctetStringBuffer {
uint8_t *buffer;
size_t buffer_size;
uint32_t buffer_length;
} BACNET_OCTET_STRING_BUFFER;
/* max size of a BACnet tag */
#define BACNET_TAG_SIZE 7
+96
View File
@@ -1377,6 +1377,102 @@ bool octetstring_value_same(
}
#endif
/**
* @brief Duplicates an octet string value and length to a buffer structure.
* @param dest Pointer to destination buffer structure.
* @param value Pointer to the byte array to be copied to the buffer structure.
* @param length Count of bytes to be copied to the buffer structure.
* @return true if copy is successful.
*/
bool octetstring_buffer_duplicate(
BACNET_OCTET_STRING_BUFFER *dest, const uint8_t *value, size_t length)
{
uint8_t *new_buffer = NULL;
bool status = false;
if (dest && value) {
if (dest->buffer && (length <= dest->buffer_size)) {
dest->buffer_length = length;
if (length > 0) {
memcpy(dest->buffer, value, length);
}
status = true;
} else if (length > 0) {
new_buffer = realloc(dest->buffer, length);
if (new_buffer) {
dest->buffer = new_buffer;
dest->buffer_length = length;
dest->buffer_size = length;
memcpy(dest->buffer, value, length);
status = true;
}
} else {
/* length is zero, so just set the length and return true */
dest->buffer_length = 0;
status = true;
}
}
return status;
}
/**
* @brief Duplicates an octet string value to a buffer structure.
* @param dest Pointer to destination buffer structure.
* @param src Pointer to source octet string structure.
* @return true if copy is successful.
*/
bool octetstring_to_buffer_duplicate(
BACNET_OCTET_STRING_BUFFER *dest, const BACNET_OCTET_STRING *src)
{
if (!src) {
return false;
}
return octetstring_buffer_duplicate(dest, src->value, src->length);
}
/**
* @brief Copies an octet string value to a buffer structure.
* @param dest Pointer to destination buffer structure.
* @param src Pointer to source octet string structure.
* @return true if copy is successful.
*/
bool octetstring_to_buffer_copy(
BACNET_OCTET_STRING_BUFFER *dest, const BACNET_OCTET_STRING *src)
{
bool status = false;
if (dest && src) {
if (src->length <= dest->buffer_size) {
dest->buffer_length = src->length;
if (src->length > 0) {
memcpy(dest->buffer, src->value, src->length);
}
status = true;
}
}
return status;
}
/**
* @brief Copies an octet string value from a buffer structure.
* @param dest Pointer to destination octet string structure.
* @param src Pointer to source buffer structure.
* @return true if copy is successful.
*/
bool octetstring_from_buffer_copy(
BACNET_OCTET_STRING *dest, const BACNET_OCTET_STRING_BUFFER *src)
{
if (dest && src && src->buffer &&
(src->buffer_length <= sizeof(dest->value))) {
memcpy(dest->value, src->buffer, src->buffer_length);
dest->length = src->buffer_length;
return true;
}
return false;
}
/**
* @brief Compare two strings, case sensitive or insensitive, with length limit
* @details The strncmp() function compares, at most, the first n characters
+30
View File
@@ -21,17 +21,34 @@ typedef struct BACnet_Bit_String {
uint8_t value[MAX_BITSTRING_BYTES];
} BACNET_BIT_STRING;
/* fixed size buffer version of Character String */
typedef struct BACnet_Character_String {
size_t length;
uint8_t encoding;
char value[MAX_CHARACTER_STRING_BYTES];
} BACNET_CHARACTER_STRING;
/* fixed size buffer version of Octet String */
typedef struct BACnet_Octet_String {
size_t length;
uint8_t value[MAX_OCTET_STRING_BYTES];
} BACNET_OCTET_STRING;
/* buffer pointer version of Character String */
typedef struct BACnetCharacterStringBuffer {
uint8_t encoding;
char *buffer;
size_t buffer_size;
uint32_t buffer_length;
} BACNET_CHARACTER_STRING_BUFFER;
/* buffer pointer version of Octet String */
typedef struct BACnetOctetStringBuffer {
uint8_t *buffer;
size_t buffer_size;
uint32_t buffer_length;
} BACNET_OCTET_STRING_BUFFER;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
@@ -183,6 +200,19 @@ bool octetstring_value_same(
const BACNET_OCTET_STRING *octet_string1,
const BACNET_OCTET_STRING *octet_string2);
BACNET_STACK_EXPORT
bool octetstring_buffer_duplicate(
BACNET_OCTET_STRING_BUFFER *dest, const uint8_t *value, size_t length);
BACNET_STACK_EXPORT
bool octetstring_to_buffer_duplicate(
BACNET_OCTET_STRING_BUFFER *dest, const BACNET_OCTET_STRING *src);
BACNET_STACK_EXPORT
bool octetstring_to_buffer_copy(
BACNET_OCTET_STRING_BUFFER *dest, const BACNET_OCTET_STRING *src);
BACNET_STACK_EXPORT
bool octetstring_from_buffer_copy(
BACNET_OCTET_STRING *dest, const BACNET_OCTET_STRING_BUFFER *src);
BACNET_STACK_EXPORT
int bacnet_strcmp(const char *a, const char *b);
BACNET_STACK_EXPORT
+199 -55
View File
@@ -15,18 +15,20 @@
/* BACnet Stack API */
#include "bacnet/bacdcode.h"
#include "bacnet/bacapp.h"
#include "bacnet/bacstr.h"
#include "bacnet/bactext.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/sys/keylist.h"
#include "bacnet/basic/object/osv.h"
typedef struct octetstring_value_descr {
struct object_data {
unsigned Event_State : 3;
bool Out_Of_Service;
BACNET_OCTET_STRING Present_Value;
const char *Object_Name;
} OCTETSTRING_VALUE_DESCR;
bool Out_Of_Service : 1;
BACNET_OCTET_STRING_BUFFER Present_Value;
char *Object_Name;
char *Description;
};
/* Key List for storing object data sorted by instance number */
static OS_Keylist Object_List = NULL;
@@ -97,8 +99,7 @@ void OctetString_Value_Writable_Property_List(
* @param object_instance Object instance number.
* @return Pointer to object descriptor, or NULL if not found.
*/
static OCTETSTRING_VALUE_DESCR *
OctetString_Value_Object(uint32_t object_instance)
static struct object_data *OctetString_Value_Object(uint32_t object_instance)
{
return Keylist_Data(Object_List, object_instance);
}
@@ -111,7 +112,7 @@ OctetString_Value_Object(uint32_t object_instance)
*/
uint32_t OctetString_Value_Create(uint32_t object_instance)
{
OCTETSTRING_VALUE_DESCR *pObject = NULL;
struct object_data *pObject = NULL;
int index = 0;
if (!Object_List) {
@@ -124,7 +125,7 @@ uint32_t OctetString_Value_Create(uint32_t object_instance)
}
pObject = OctetString_Value_Object(object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(OCTETSTRING_VALUE_DESCR));
pObject = calloc(1, sizeof(struct object_data));
if (!pObject) {
return BACNET_MAX_INSTANCE;
}
@@ -133,8 +134,6 @@ uint32_t OctetString_Value_Create(uint32_t object_instance)
free(pObject);
return BACNET_MAX_INSTANCE;
}
octetstring_init(&pObject->Present_Value, NULL, 0);
pObject->Out_Of_Service = false;
pObject->Event_State = EVENT_STATE_NORMAL;
}
@@ -148,10 +147,13 @@ uint32_t OctetString_Value_Create(uint32_t object_instance)
*/
bool OctetString_Value_Delete(uint32_t object_instance)
{
OCTETSTRING_VALUE_DESCR *pObject = NULL;
struct object_data *pObject = NULL;
pObject = Keylist_Data_Delete(Object_List, object_instance);
if (pObject) {
free(pObject->Description);
free(pObject->Object_Name);
free(pObject->Present_Value.buffer);
free(pObject);
return true;
}
@@ -231,42 +233,19 @@ bool OctetString_Value_Present_Value_Set(
const BACNET_OCTET_STRING *value,
uint8_t priority)
{
OCTETSTRING_VALUE_DESCR *pObject = NULL;
struct object_data *pObject = NULL;
bool status = false;
(void)priority;
pObject = OctetString_Value_Object(object_instance);
if (pObject) {
octetstring_copy(&pObject->Present_Value, value);
octetstring_to_buffer_duplicate(&pObject->Present_Value, value);
status = true;
}
return status;
}
/**
* @brief Sets the present value length for an Octet String Value object.
* @param object_instance Object instance number.
* @param value Pointer to octet string value.
* @param length Length of the octet string value.
* @param priority Write priority (1..16).
* @return true if values are within range and present value length is set.
*/
bool OctetString_Value_Present_Value_Length_Set(
uint32_t object_instance, uint8_t *value, size_t length, uint8_t priority)
{
OCTETSTRING_VALUE_DESCR *pObject = NULL;
bool status = false;
(void)priority;
pObject = OctetString_Value_Object(object_instance);
if (pObject) {
status = octetstring_init(&pObject->Present_Value, value, length);
}
return status;
}
/**
* @brief Gets the present value for an Octet String Value object.
* @param object_instance Object instance number.
@@ -276,40 +255,70 @@ bool OctetString_Value_Present_Value_Length_Set(
bool OctetString_Value_Present_Value_Get(
uint32_t object_instance, BACNET_OCTET_STRING *value)
{
OCTETSTRING_VALUE_DESCR *pObject = NULL;
struct object_data *pObject = NULL;
bool status = false;
pObject = OctetString_Value_Object(object_instance);
if (pObject) {
status = octetstring_copy(value, &pObject->Present_Value);
status = octetstring_from_buffer_copy(value, &pObject->Present_Value);
}
return status;
}
/**
* @brief Gets the present value length for an Octet String Value object.
* @brief Sets the present value buffer and length for this object.
* @param object_instance Object instance number.
* @param value Pointer to octet string value.
* @param length Length of the octet string value.
* @param priority Write priority (1..16).
* @return true if values are within range and present value length is set.
*/
bool OctetString_Value_Present_Value_Buffer_Set(
uint32_t object_instance, uint8_t *value, size_t length, uint8_t priority)
{
struct object_data *pObject = NULL;
bool status = false;
(void)priority;
pObject = OctetString_Value_Object(object_instance);
if (pObject) {
status = octetstring_buffer_duplicate(
&pObject->Present_Value, value, length);
}
return status;
}
/**
* @brief Gets the present value buffer and length for this object.
* @param object_instance Object instance number.
* @param value Pointer to buffer to receive octet string value.
* @param value_size Size of the value buffer.
* @param length Pointer to receive length of the octet string value.
* @return true if object exists and value length is returned.
*/
bool OctetString_Value_Present_Value_Length(
bool OctetString_Value_Present_Value_Buffer_Get(
uint32_t object_instance, uint8_t *value, size_t value_size, size_t *length)
{
OCTETSTRING_VALUE_DESCR *pObject = NULL;
size_t value_length = 0;
struct object_data *pObject = NULL;
bool status = false;
size_t copy_length = 0;
pObject = OctetString_Value_Object(object_instance);
if (pObject) {
value_length = octetstring_length(&pObject->Present_Value);
if (length) {
*length = value_length;
*length = pObject->Present_Value.buffer_length;
}
if (value && (value_size >= value_length)) {
memcpy(value, pObject->Present_Value.value, value_length);
if (value) {
if (value_size > pObject->Present_Value.buffer_length) {
copy_length = pObject->Present_Value.buffer_length;
} else {
copy_length = value_size;
}
if (copy_length > 0) {
memcpy(value, pObject->Present_Value.buffer, copy_length);
}
}
status = true;
}
@@ -328,7 +337,7 @@ bool OctetString_Value_Object_Name(
{
char text[32] = "";
bool status = false;
OCTETSTRING_VALUE_DESCR *pObject = NULL;
struct object_data *pObject = NULL;
pObject = OctetString_Value_Object(object_instance);
if (pObject) {
@@ -356,12 +365,13 @@ bool OctetString_Value_Object_Name(
bool OctetString_Value_Name_Set(uint32_t object_instance, const char *new_name)
{
bool status = false; /* return value */
OCTETSTRING_VALUE_DESCR *pObject;
struct object_data *pObject;
pObject = OctetString_Value_Object(object_instance);
if (pObject) {
status = true;
pObject->Object_Name = new_name;
free(pObject->Object_Name);
pObject->Object_Name = bacnet_strdup(new_name);
}
return status;
@@ -375,7 +385,7 @@ bool OctetString_Value_Name_Set(uint32_t object_instance, const char *new_name)
const char *OctetString_Value_Name_ASCII(uint32_t object_instance)
{
const char *name = NULL;
OCTETSTRING_VALUE_DESCR *pObject;
struct object_data *pObject;
pObject = OctetString_Value_Object(object_instance);
if (pObject) {
@@ -385,6 +395,54 @@ const char *OctetString_Value_Name_ASCII(uint32_t object_instance)
return name;
}
/**
* For a given object instance-number, returns the description
*
* @param object_instance - object-instance number of the object
*
* @return description text or NULL if not found
*/
const char *OctetString_Value_Description(uint32_t object_instance)
{
const char *name = NULL;
const struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Description) {
name = pObject->Description;
} else {
name = "";
}
}
return name;
}
/**
* For a given object instance-number, sets the description
*
* @param object_instance - object-instance number of the object
* @param new_name - holds the description to be set
*
* @return true if description was set
*/
bool OctetString_Value_Description_Set(
uint32_t object_instance, const char *new_name)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = true;
free(pObject->Description);
pObject->Description = bacnet_strdup(new_name);
}
return status;
}
/**
* For a given object instance-number, returns the out-of-service
* property value
@@ -396,7 +454,7 @@ const char *OctetString_Value_Name_ASCII(uint32_t object_instance)
bool OctetString_Value_Out_Of_Service(uint32_t object_instance)
{
bool value = false;
OCTETSTRING_VALUE_DESCR *pObject;
struct object_data *pObject;
pObject = OctetString_Value_Object(object_instance);
if (pObject) {
@@ -416,7 +474,7 @@ bool OctetString_Value_Out_Of_Service(uint32_t object_instance)
*/
bool OctetString_Value_Out_Of_Service_Set(uint32_t object_instance, bool value)
{
OCTETSTRING_VALUE_DESCR *pObject;
struct object_data *pObject;
pObject = OctetString_Value_Object(object_instance);
if (pObject) {
@@ -452,7 +510,6 @@ int OctetString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
break;
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
OctetString_Value_Object_Name(
rpdata->object_instance, &char_string);
apdu_len =
@@ -487,10 +544,18 @@ int OctetString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(
&char_string,
OctetString_Value_Description(rpdata->object_instance));
apdu_len = encode_application_character_string(apdu, &char_string);
break;
case PROP_OUT_OF_SERVICE:
state = OctetString_Value_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
@@ -501,6 +566,70 @@ int OctetString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
return apdu_len;
}
/**
* For a given object instance-number, sets the object-name
*
* @param object_instance - object-instance number of the object
* @param cstring - holds the object-name to be set
*
* @return true if object-name was set
*/
static bool OctetString_Value_Object_Name_Write(
BACNET_WRITE_PROPERTY_DATA *wp_data, BACNET_CHARACTER_STRING *cstring)
{
bool status = false; /* return value */
struct object_data *pObject;
char *utf8_name = NULL;
pObject = Keylist_Data(Object_List, wp_data->object_instance);
if (pObject) {
utf8_name =
write_property_characterstring_utf8_strdup(wp_data, cstring);
if (utf8_name) {
free(pObject->Object_Name);
pObject->Object_Name = utf8_name;
status = true;
}
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return status;
}
/**
* For a given object instance-number, sets the description property value
*
* @param object_instance - object-instance number of the object
* @param cstring - holds the description to be set
*
* @return true if description was set
*/
static bool OctetString_Value_Description_Write(
BACNET_WRITE_PROPERTY_DATA *wp_data, BACNET_CHARACTER_STRING *cstring)
{
bool status = false; /* return value */
struct object_data *pObject;
char *utf8_name = NULL;
pObject = Keylist_Data(Object_List, wp_data->object_instance);
if (pObject) {
utf8_name =
write_property_characterstring_utf8_strdup(wp_data, cstring);
if (utf8_name) {
free(pObject->Description);
pObject->Description = utf8_name;
status = true;
}
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return status;
}
/**
* @brief Processes a write-property request for an Octet String Value object.
* @param wp_data Write property request/response context.
@@ -551,7 +680,22 @@ bool OctetString_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
}
}
break;
case PROP_OBJECT_NAME:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_CHARACTER_STRING);
if (status) {
status = OctetString_Value_Object_Name_Write(
wp_data, &value.type.Character_String);
}
break;
case PROP_DESCRIPTION:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_CHARACTER_STRING);
if (status) {
status = OctetString_Value_Description_Write(
wp_data, &value.type.Character_String);
}
break;
case PROP_OUT_OF_SERVICE:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN);
+3 -3
View File
@@ -65,10 +65,10 @@ BACNET_STACK_EXPORT
BACNET_OCTET_STRING *OctetString_Value_Present_Value(uint32_t object_instance);
BACNET_STACK_EXPORT
bool OctetString_Value_Present_Value_Length_Set(
bool OctetString_Value_Present_Value_Buffer_Set(
uint32_t object_instance, uint8_t *value, size_t length, uint8_t priority);
BACNET_STACK_EXPORT
bool OctetString_Value_Present_Value_Length(
bool OctetString_Value_Present_Value_Buffer_Get(
uint32_t object_instance,
uint8_t *value,
size_t value_size,
@@ -83,7 +83,7 @@ bool OctetString_Value_Encode_Value_List(
uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list);
BACNET_STACK_EXPORT
char *OctetString_Value_Description(uint32_t instance);
const char *OctetString_Value_Description(uint32_t instance);
BACNET_STACK_EXPORT
bool OctetString_Value_Description_Set(uint32_t instance, const char *new_name);
+2 -2
View File
@@ -203,7 +203,7 @@ bool Structured_View_Object_Name(
{
bool status = false;
struct object_data *pObject;
char name_text[48] = "Structured-View-4194303";
char name_text[48] = "";
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
@@ -293,7 +293,7 @@ const char *Structured_View_Description(uint32_t object_instance)
* @param object_instance - object-instance number of the object
* @param new_name - holds the description to be set
*
* @return true if object-name was set
* @return true if description was set
*/
bool Structured_View_Description_Set(
uint32_t object_instance, const char *new_name)