Feature/comments and review (#102)

* Added comments. Replaced -1 with BACNET_STATUS_ERROR macro.

* Added some DogyGen comments

* Added missing return value check for booleans.

* Check used bits against unused bits in bitstrings.

* Added missing return value check for bitstrings.

* Added sanity checks and comments.

* Fixed implicit type conv warning.

* Chose to keep simpler C function methods.

Co-authored-by: Steve Karg <steve@kargs.net>
Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Roy Schneider
2020-07-14 18:41:51 +02:00
committed by GitHub
parent de7f7e9782
commit 2af16ab14d
5 changed files with 838 additions and 234 deletions
+385 -94
View File
@@ -48,65 +48,105 @@
#define BACNET_STRING_UTF8_VALIDATION 1
#endif
/**
* Initialize a bit string.
*
* @param bit_string Pointer to the bit string structure.
*/
void bitstring_init(BACNET_BIT_STRING *bit_string)
{
int i;
bit_string->bits_used = 0;
for (i = 0; i < MAX_BITSTRING_BYTES; i++) {
bit_string->value[i] = 0;
if (bit_string) {
bit_string->bits_used = 0;
for (i = 0; i < MAX_BITSTRING_BYTES; i++) {
bit_string->value[i] = 0;
}
}
}
/**
* Set bits in the bit string.
*
* @param bit_string Pointer to the bit string structure.
* @param bit_number Number of the bit [0..(MAX_BITSTRING_BYTES*8)-1]
* @param value Value 0/1
*/
void bitstring_set_bit(
BACNET_BIT_STRING *bit_string, uint8_t bit_number, bool value)
{
uint8_t byte_number = bit_number / 8;
uint8_t bit_mask = 1;
if (byte_number < MAX_BITSTRING_BYTES) {
/* set max bits used */
if (bit_string->bits_used < (bit_number + 1)) {
bit_string->bits_used = bit_number + 1;
}
bit_mask = bit_mask << (bit_number - (byte_number * 8));
if (value) {
bit_string->value[byte_number] |= bit_mask;
} else {
bit_string->value[byte_number] &= (~(bit_mask));
if (bit_string) {
if (byte_number < MAX_BITSTRING_BYTES) {
/* set max bits used */
if (bit_string->bits_used < (bit_number + 1)) {
bit_string->bits_used = bit_number + 1;
}
bit_mask = bit_mask << (bit_number - (byte_number * 8));
if (value) {
bit_string->value[byte_number] |= bit_mask;
} else {
bit_string->value[byte_number] &= (~(bit_mask));
}
}
}
}
/**
* Return the value of a single bit
* out of the bit string.
*
* @param bit_string Pointer to the bit string structure.
* @param bit_number Number of the bit [0..(MAX_BITSTRING_BYTES*8)-1]
*
* @return Value 0/1
*/
bool bitstring_bit(BACNET_BIT_STRING *bit_string, uint8_t bit_number)
{
bool value = false;
uint8_t byte_number = bit_number / 8;
uint8_t bit_mask = 1;
if (bit_number < (MAX_BITSTRING_BYTES * 8)) {
bit_mask = bit_mask << (bit_number - (byte_number * 8));
if (bit_string->value[byte_number] & bit_mask) {
value = true;
if (bit_string) {
if (bit_number < (MAX_BITSTRING_BYTES * 8)) {
bit_mask = bit_mask << (bit_number - (byte_number * 8));
if (bit_string->value[byte_number] & bit_mask) {
value = true;
}
}
}
return value;
}
/**
* Return the number of bits used.
*
* @param bit_string Pointer to the bit string structure.
*
* @return Bits used [0..(MAX_BITSTRING_BYTES*8)-1]
*/
uint8_t bitstring_bits_used(BACNET_BIT_STRING *bit_string)
{
return bit_string->bits_used;
return (bit_string ? bit_string->bits_used : 0);
}
/* returns the number of bytes that a bit string is using */
/**
* Returns the number of bytes that a bit string is using.
*
* @param bit_string Pointer to the bit string structure.
*
* @return Bytes used [0..MAX_BITSTRING_BYTES]
*/
uint8_t bitstring_bytes_used(BACNET_BIT_STRING *bit_string)
{
uint8_t len = 0; /* return value */
uint8_t used_bytes = 0;
uint8_t last_bit = 0;
if (bit_string->bits_used) {
if (bit_string && bit_string->bits_used) {
last_bit = bit_string->bits_used - 1;
used_bytes = last_bit / 8;
/* add one for the first byte */
@@ -117,6 +157,14 @@ uint8_t bitstring_bytes_used(BACNET_BIT_STRING *bit_string)
return len;
}
/**
* Returns an octed at the given bit position.
*
* @param bit_string Pointer to the bit string structure.
* @param octet_index Byte index of the octed [0..MAX_BITSTRING_BYTES-1]
*
* @return Value of the octed.
*/
uint8_t bitstring_octet(BACNET_BIT_STRING *bit_string, uint8_t octet_index)
{
uint8_t octet = 0;
@@ -130,6 +178,15 @@ uint8_t bitstring_octet(BACNET_BIT_STRING *bit_string, uint8_t octet_index)
return octet;
}
/**
* Set an octed at the given bit position.
*
* @param bit_string Pointer to the bit string structure.
* @param index Byte index of the octed [0..MAX_BITSTRING_BYTES-1]
* @param octet Octet value
*
* @return true on success, false otherwise.
*/
bool bitstring_set_octet(
BACNET_BIT_STRING *bit_string, uint8_t index, uint8_t octet)
{
@@ -145,13 +202,25 @@ bool bitstring_set_octet(
return status;
}
/**
* Write the amount of bits used in the bit
* string structure.
*
* @param bit_string Pointer to the bit string structure.
* @param bytes_used Count of bytes used.
* @param unused_bits Count of remaining unused bits in
* the last byte.
*
* @return true on success or false on error.
*/
bool bitstring_set_bits_used(
BACNET_BIT_STRING *bit_string, uint8_t bytes_used, uint8_t unused_bits)
BACNET_BIT_STRING * bit_string,
uint8_t bytes_used,
uint8_t unused_bits)
{
bool status = false;
if (bit_string) {
/* FIXME: check that bytes_used is at least one? */
if (bit_string && bytes_used) {
bit_string->bits_used = bytes_used * 8;
bit_string->bits_used -= unused_bits;
status = true;
@@ -160,6 +229,13 @@ bool bitstring_set_bits_used(
return status;
}
/**
* Return the capcity of the bit string.
*
* @param bit_string Pointer to the bit string structure.
*
* @return Capacitiy in bits [0..(MAX_BITSTRING_BYTES*8)]
*/
uint8_t bitstring_bits_capacity(BACNET_BIT_STRING *bit_string)
{
if (bit_string) {
@@ -169,6 +245,14 @@ uint8_t bitstring_bits_capacity(BACNET_BIT_STRING *bit_string)
}
}
/**
* Copy bits from one bit string to another.
*
* @param dest Pointer to the destination bit string structure.
* @param src Pointer to the source bit string structure.
*
* @return true on success, false otherwise.
*/
bool bitstring_copy(BACNET_BIT_STRING *dest, BACNET_BIT_STRING *src)
{
unsigned i;
@@ -185,25 +269,34 @@ bool bitstring_copy(BACNET_BIT_STRING *dest, BACNET_BIT_STRING *src)
return status;
}
/* returns true if the same length and contents */
/**
* Returns true if the same length and contents.
*
* @param bitstring1 Pointer to the first bit string structure.
* @param bitstring2 Pointer to the second bit string structure.
*
* @return true if the content of both bit strings are
* the same, false otherwise.
*/
bool bitstring_same(
BACNET_BIT_STRING *bitstring1, BACNET_BIT_STRING *bitstring2)
{
int i = 0; /* loop counter */
int i; /* loop counter */
int bytes_used = 0;
uint8_t compare_mask = 0;
if (bitstring1 && bitstring2) {
bytes_used = (int)(bitstring1->bits_used / 8);
if ((bitstring1->bits_used == bitstring2->bits_used) &&
(bitstring1->bits_used / 8 <= MAX_BITSTRING_BYTES)) {
bytes_used = (int)(bitstring1->bits_used / 8);
compare_mask = 0xFF >> (8 - (bitstring1->bits_used % 8));
(bytes_used <= MAX_BITSTRING_BYTES)) {
/* compare fully used bytes */
for (i = 0; i < bytes_used; i++) {
if (bitstring1->value[i] != bitstring2->value[i]) {
return false;
}
}
/* compare only the relevant bits of last partly used byte */
compare_mask = 0xFF >> (8 - (bitstring1->bits_used % 8));
if ((bitstring1->value[bytes_used] & compare_mask) !=
(bitstring2->value[bytes_used] & compare_mask)) {
return false;
@@ -217,9 +310,18 @@ bool bitstring_same(
}
#if PRINT_ENABLED
/* converts an null terminated ASCII string to an bitstring.
Expects "1,0,1,0,1,1" or "101011" as the bits
returns true if successfully converted and fits; false if too long */
/**
* Converts an null terminated ASCII string to an bitstring.
*
* Expects "1,0,1,0,1,1" or "101011" as the bits
*
* @param bit_string Pointer to the bit string structure.
* @param ascii Pointer to a zero terminated string, made up from
* '0' and '1' like "010010011", that shall be
* converted into a bit string.
*
* @return true if successfully converted and fits; false if too long.
*/
bool bitstring_init_ascii(BACNET_BIT_STRING *bit_string, const char *ascii)
{
bool status = false; /* return value */
@@ -262,8 +364,19 @@ bool bitstring_init_ascii(BACNET_BIT_STRING *bit_string, const char *ascii)
#endif
#define CHARACTER_STRING_CAPACITY (MAX_CHARACTER_STRING_BYTES - 1)
/* returns false if the string exceeds capacity
initialize by using value=NULL */
/**
* Initialize a BACnet characater string.
* Returns false if the string exceeds capacity.
* Initialize by using value=NULL
*
* @param char_string Pointer to the BACnet string
* @param encoding Encoding that shall be used
* like CHARACTER_UTF8
* @param value C-string used to initialize the object
* @param length C-String length in characters.
*
* @return true on success, false if the string exceeds capacity.
*/
bool characterstring_init(BACNET_CHARACTER_STRING *char_string,
uint8_t encoding,
const char *value,
@@ -299,6 +412,17 @@ bool characterstring_init(BACNET_CHARACTER_STRING *char_string,
return status;
}
/**
* Initialize a BACnet characater string.
* Returns false if the string exceeds capacity.
* Initialize by using value=NULL
*
* @param char_string Pointer to the BACnet string
* @param value C-string used to initialize the object
* @param tmax C-String length in characters.
*
* @return true/false
*/
bool characterstring_init_ansi_safe(
BACNET_CHARACTER_STRING *char_string, const char *value, size_t tmax)
{
@@ -306,6 +430,16 @@ bool characterstring_init_ansi_safe(
value ? strnlen(value, tmax) : 0);
}
/**
* Initialize a BACnet characater string.
* Returns false if the string exceeds capacity.
* Initialize by using value=NULL
*
* @param char_string Pointer to the BACnet string
* @param value C-string used to initialize the object
*
* @return true/false
*/
bool characterstring_init_ansi(
BACNET_CHARACTER_STRING *char_string, const char *value)
{
@@ -313,34 +447,64 @@ bool characterstring_init_ansi(
char_string, CHARACTER_ANSI_X34, value, value ? strlen(value) : 0);
}
/**
* Copy a character string.
*
* @param dest Pointer to the destination string.
* @param src Pointer to the source string.
*
* @return true/false
*/
bool characterstring_copy(
BACNET_CHARACTER_STRING *dest, BACNET_CHARACTER_STRING *src)
{
return characterstring_init(dest, characterstring_encoding(src),
characterstring_value(src), characterstring_length(src));
}
bool characterstring_ansi_copy(
char *dest, size_t dest_max_len, BACNET_CHARACTER_STRING *src)
{
size_t i; /* counter */
if (dest && src && (src->encoding == CHARACTER_ANSI_X34) &&
(src->length < dest_max_len)) {
for (i = 0; i < dest_max_len; i++) {
if (i < src->length) {
dest[i] = src->value[i];
} else {
dest[i] = 0;
}
}
return true;
if (dest && src) {
return characterstring_init(dest, characterstring_encoding(src),
characterstring_value(src), characterstring_length(src));
}
return false;
}
/* returns true if the character encoding and string contents are the same */
/**
* Copy a character string into a C-string.
*
* @param dest Pointer to the destination C-string buffer.
* @param dest_max_len Size of the destination C-string buffer.
* @param src Pointer to the source BACnet string.
*
* @return true/false
*/
bool characterstring_ansi_copy(
char *dest, size_t dest_max_len, BACNET_CHARACTER_STRING *src)
{
size_t i; /* counter */
if (dest && src) {
if ((src->encoding == CHARACTER_ANSI_X34) && (src->length < dest_max_len)) {
for (i = 0; i < dest_max_len; i++) {
if (i < src->length) {
dest[i] = src->value[i];
} else {
dest[i] = 0;
}
}
return true;
}
}
return false;
}
/**
* Returns true if the character encoding and string
* contents are the same.
*
* @param dest Pointer to the first string to test.
* @param src Pointer to the second string to test.
*
* @return true if the character encoding and string contents are the same
*/
bool characterstring_same(
BACNET_CHARACTER_STRING *dest, BACNET_CHARACTER_STRING *src)
{
@@ -348,12 +512,14 @@ bool characterstring_same(
bool same_status = false;
if (src && dest) {
if ((src->length == dest->length) &&
(src->encoding == dest->encoding)) {
if ((src->encoding == dest->encoding) &&
(src->length == dest->length) &&
(src->length <= MAX_CHARACTER_STRING_BYTES)) {
same_status = true;
for (i = 0; (i < src->length) && same_status; i++) {
for (i = 0; i < src->length; i++) {
if (src->value[i] != dest->value[i]) {
same_status = false;
break;
}
}
}
@@ -370,18 +536,29 @@ bool characterstring_same(
return same_status;
}
/**
* Returns true if the BACnet string and the C-string
* contents are the same.
*
* @param dest Pointer to the first string to test.
* @param src Pointer to the second string to test.
*
* @return true if the character encoding and string contents are the same
*/
bool characterstring_ansi_same(BACNET_CHARACTER_STRING *dest, const char *src)
{
size_t i; /* counter */
bool same_status = false;
if (src && dest) {
if ((dest->length == strlen(src)) &&
(dest->encoding == CHARACTER_ANSI_X34)) {
if ((dest->encoding == CHARACTER_ANSI_X34) &&
(dest->length == strlen(src)) &&
(dest->length <= MAX_CHARACTER_STRING_BYTES)) {
same_status = true;
for (i = 0; (i < dest->length) && same_status; i++) {
for (i = 0; i < dest->length; i++) {
if (src[i] != dest->value[i]) {
same_status = false;
break;
}
}
}
@@ -400,7 +577,18 @@ bool characterstring_ansi_same(BACNET_CHARACTER_STRING *dest, const char *src)
return same_status;
}
/* returns false if the string exceeds capacity */
/**
* Append some characters to the end of the characterstring
*
* @param char_string Pointer to the BACnet string to which
* the content of the C-string shall be added.
* @param value Pointer to the C-String to be added.
* @param length Count of characters to add.
*
* @param src Pointer to the first string to test.
*
* @return false if the string exceeds capacity.
*/
bool characterstring_append(
BACNET_CHARACTER_STRING *char_string, const char *value, size_t length)
{
@@ -420,9 +608,14 @@ bool characterstring_append(
return status;
}
/* This function sets a new length without changing the value.
If length exceeds capacity, no modification happens and
function returns false. */
/**
* @brief This function sets a new length without changing
* the value. If length exceeds capacity, no modification
* happens and function returns false.
*
* @return true on success, false if the string exceeds
* capacity.
*/
bool characterstring_truncate(
BACNET_CHARACTER_STRING *char_string, size_t length)
{
@@ -587,8 +780,8 @@ bool characterstring_printable(BACNET_CHARACTER_STRING *char_string)
#if BACNET_STRING_UTF8_VALIDATION
/* Basic UTF-8 manipulation routines
by Jeff Bezanson
placed in the public domain Fall 2005 */
* by Jeff Bezanson
* placed in the public domain Fall 2005 */
static const char trailingBytesForUTF8[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -601,19 +794,33 @@ static const char trailingBytesForUTF8[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 };
/* based on the valid_utf8 routine from the PCRE library by Philip Hazel
length is in bytes, since without knowing whether the string is valid
it's hard to know how many characters there are! */
/**
* @brief Based on the valid_utf8 routine from the PCRE library by Philip Hazel
* length is in bytes, since without knowing whether the string is valid
* it's hard to know how many characters there are!
*
* @param str Pointer to the character string.
* @param length Count of bytes to check. The count of bytes
* does not necessarily match the count of chars.
*
* @return true if the string is valid, false otherwise.
*/
bool utf8_isvalid(const char *str, size_t length)
{
const unsigned char *p, *pend = (unsigned char *)str + length;
const unsigned char *p, *pend;
unsigned char c;
size_t ab;
/* empty string is valid */
/* An empty string is valid. */
if (length == 0) {
return true;
}
/* Check pointer. */
if (!str) {
return false;
}
/* Check characters. */
pend = (unsigned char *) str + length;
for (p = (const unsigned char *)str; p < pend; p++) {
c = *p;
/* null in middle of string */
@@ -696,26 +903,45 @@ bool utf8_isvalid(const char *str, size_t length)
}
#endif
/**
* Check if the character string is valid or not.
*
* @param char_string Pointer to the character string.
*
* @return true if the string is valid, false otherwise.
*/
bool characterstring_valid(BACNET_CHARACTER_STRING *char_string)
{
bool valid = false; /* return value */
if (char_string->encoding < MAX_CHARACTER_STRING_ENCODING) {
if (char_string->encoding == CHARACTER_UTF8) {
if (utf8_isvalid(char_string->value, char_string->length)) {
if (char_string) {
if (char_string->encoding < MAX_CHARACTER_STRING_ENCODING) {
if (char_string->encoding == CHARACTER_UTF8) {
/*UTF8 check*/
if (utf8_isvalid(char_string->value, char_string->length)) {
valid = true;
}
} else {
/*non UTF8*/
valid = true;
}
} else {
valid = true;
}
}
return valid;
}
#if BACNET_USE_OCTETSTRING
/* returns false if the string exceeds capacity
initialize by using value=NULL */
/**
* @brief Initialize an octed string with the given bytes or
* zeros, if NULL for the value is provided.
*
* @param octet_string Pointer to the octed string.
* @param value Pointer to the bytes to be copied to the octed
* string or NULL to initialize the octed string.
* @param length Count of bytes used to fill the octed string.
*
* @return true on success, false if the string exceeds capacity.
*/
bool octetstring_init(
BACNET_OCTET_STRING *octet_string, uint8_t *value, size_t length)
{
@@ -746,8 +972,12 @@ bool octetstring_init(
}
#if PRINT_ENABLED
/* converts an null terminated ASCII Hex string to an octet string.
returns true if successfully converted and fits; false if too long */
/** @brief Converts an null terminated ASCII Hex string to an octet string.
*
* @param octet_string Pointer to the octed string.
* @param ascii_hex Pointer to the HEx-ASCII string.
*
* @return true if successfully converted and fits; false if too long */
bool octetstring_init_ascii_hex(
BACNET_OCTET_STRING *octet_string, const char *ascii_hex)
{
@@ -756,7 +986,7 @@ bool octetstring_init_ascii_hex(
uint8_t value = 0;
char hex_pair_string[3] = "";
if (octet_string) {
if (octet_string && ascii_hex) {
octet_string->length = 0;
if (ascii_hex[0] == 0) {
/* nothing to decode, so success! */
@@ -793,14 +1023,31 @@ bool octetstring_init_ascii_hex(
}
#endif
/**
* Copy an octed string from source to destination.
*
* @param dest Pointer to the destination octed string.
* @param src Pointer to the source octed string.
*
* @return true on success, false otherwise.
*/
bool octetstring_copy(BACNET_OCTET_STRING *dest, BACNET_OCTET_STRING *src)
{
return octetstring_init(
dest, octetstring_value(src), octetstring_length(src));
}
/* returns the number of bytes copied, or 0 if the dest
cannot hold entire octetstring value */
/**
* @brief Copy bytes from the octed string to a byte buffer.
*
* @param dest Pointer to the byte buffer.
* @param length Bytes to be copied from the
* octed string to the buffer.
* @param src Pointer to the octed string.
*
* @return Returns the number of bytes copied, or 0 if
* the dest cannot hold entire octetstring value.
*/
size_t octetstring_copy_value(
uint8_t *dest, size_t length, BACNET_OCTET_STRING *src)
{
@@ -819,7 +1066,15 @@ size_t octetstring_copy_value(
return bytes_copied;
}
/* returns false if the string exceeds capacity */
/**
* @brief Append bytes to the end of the octed string.
*
* @param octet_string Pointer to the octed string.
* @param value Pointer to the byte buffer to be appended.
* @param length Bytes to be appended.
*
* @return false if the string exceeds capacity.
*/
bool octetstring_append(
BACNET_OCTET_STRING *octet_string, uint8_t *value, size_t length)
{
@@ -839,9 +1094,16 @@ bool octetstring_append(
return status;
}
/* This function sets a new length without changing the value.
If length exceeds capacity, no modification happens and
function returns false. */
/**
* @brief This function sets a new length without changing the value.
* If length exceeds capacity, no modification happens and the
* function returns false.
*
* @param octet_string Pointer to the octed string.
* @param length New length the octed string is trucated to.
*
* @return tur on success, false otherwise.
*/
bool octetstring_truncate(BACNET_OCTET_STRING *octet_string, size_t length)
{
bool status = false; /* return value */
@@ -856,7 +1118,14 @@ bool octetstring_truncate(BACNET_OCTET_STRING *octet_string, size_t length)
return status;
}
/* returns a pointer to the value. */
/**
* @brief Returns a pointer to the value (data) of
* the given octed string.
*
* @param octet_string Pointer to the octed string.
*
* @return Value as a pointer to a byte array or NULL on error.
*/
uint8_t *octetstring_value(BACNET_OCTET_STRING *octet_string)
{
uint8_t *value = NULL;
@@ -868,33 +1137,55 @@ uint8_t *octetstring_value(BACNET_OCTET_STRING *octet_string)
return value;
}
/* returns the length. */
/**
* @brief Returns the length in bytes of
* the given octed string.
*
* @param octet_string Pointer to the octed string.
*
* @return Length in bytes. Returns always 0 on error.
*/
size_t octetstring_length(BACNET_OCTET_STRING *octet_string)
{
size_t length = 0;
if (octet_string) {
/* FIXME: validate length is within bounds? */
length = octet_string->length;
/* Force length to be within bounds. */
if (length > MAX_OCTET_STRING_BYTES) {
length = MAX_OCTET_STRING_BYTES;
}
}
return length;
}
/* returns the maximum capacity. */
/**
* @brief Returns the maximum capacity of an octed string.
*
* @param octet_string Pointer to the octed string.
*
* @return Capacity in bytes. Returns always 0 on error.
*/
size_t octetstring_capacity(BACNET_OCTET_STRING *octet_string)
{
size_t length = 0;
if (octet_string) {
/* FIXME: validate length is within bounds? */
length = MAX_OCTET_STRING_BYTES;
}
return length;
}
/* returns true if the same length and contents */
/**
* @brief Returns true if the same length and contents.
*
* @param octet_string1 Pointer to the first octed string.
* @param octet_string2 Pointer to the second octed string.
*
* @return true if the octed strings are the same, false otherwise.
*/
bool octetstring_value_same(
BACNET_OCTET_STRING *octet_string1, BACNET_OCTET_STRING *octet_string2)
{