/** * @file * @brief BACnet bitstring, octectstring, and characterstring encode * and decode functions * @author Steve Karg * @date 2004 * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 */ #include #include #include #include #include #include #include #include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" /* BACnet Stack API */ #include "bacnet/bacstr.h" #ifndef BACNET_STRING_UTF8_VALIDATION #define BACNET_STRING_UTF8_VALIDATION 1 #endif /* check the limits of bitstring capacity */ #if ((MAX_BITSTRING_BYTES * 8) > (UINT8_MAX + 1)) #error "MAX_BITSTRING_BYTES cannot exceed 32!" #endif #if (((MAX_BITSTRING_BYTES * 8) > UINT8_MAX) && (UINT_MAX <= UINT8_MAX)) #error "MAX_BITSTRING_BYTES cannot exceed 31!" #endif /** * Initialize a bit string. * * @param bit_string Pointer to the bit string structure. */ void bitstring_init(BACNET_BIT_STRING *bit_string) { unsigned i; 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) { unsigned byte_number = bit_number / 8; uint8_t bit_mask = 1; 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(const BACNET_BIT_STRING *bit_string, uint8_t bit_number) { bool value = false; unsigned byte_number = bit_number / 8; uint8_t bit_mask = 1; 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(const BACNET_BIT_STRING *bit_string) { return (bit_string ? bit_string->bits_used : 0); } /** * 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(const BACNET_BIT_STRING *bit_string) { uint8_t len = 0; /* return value */ uint8_t used_bytes = 0; uint8_t last_bit = 0; 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 */ used_bytes++; len = used_bytes; } return len; } /** * Returns an octet at the given bit position. * * @param bit_string Pointer to the bit string structure. * @param octet_index Byte index of the octet [0..MAX_BITSTRING_BYTES-1] * * @return Value of the octet. */ uint8_t bitstring_octet(const BACNET_BIT_STRING *bit_string, uint8_t octet_index) { uint8_t octet = 0; if (bit_string) { if (octet_index < MAX_BITSTRING_BYTES) { octet = bit_string->value[octet_index]; } } return octet; } /** * Set an octet at the given bit position. * * @param bit_string Pointer to the bit string structure. * @param index Byte index of the octet [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) { bool status = false; if (bit_string) { if (index < MAX_BITSTRING_BYTES) { bit_string->value[index] = octet; status = true; } } 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) { bool status = false; if (bit_string && bytes_used) { bit_string->bits_used = bytes_used * 8; bit_string->bits_used -= unused_bits; status = true; } return status; } /** * Return the capacity of the bit string. * * @param bit_string Pointer to the bit string structure. * * @return Capacitiy in bits [0..(MAX_BITSTRING_BYTES*8)] */ unsigned bitstring_bits_capacity(const BACNET_BIT_STRING *bit_string) { if (bit_string) { return min((MAX_BITSTRING_BYTES * 8), (UINT8_MAX + 1)); } else { return 0; } } /** * 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, const BACNET_BIT_STRING *src) { unsigned i; bool status = false; if (dest && src) { dest->bits_used = src->bits_used; for (i = 0; i < MAX_BITSTRING_BYTES; i++) { dest->value[i] = src->value[i]; } status = true; } return status; } /** * 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( const BACNET_BIT_STRING *bitstring1, const BACNET_BIT_STRING *bitstring2) { 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) && (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; } else { return true; } } } return false; } /** * 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 */ unsigned index = 0; /* offset into buffer */ uint8_t bit_number = 0; if (bit_string) { bitstring_init(bit_string); if (ascii[0] == 0) { /* nothing to decode, so success! */ status = true; } else { while (ascii[index] != 0) { if (bit_number >= bitstring_bits_capacity(bit_string)) { /* too long of a string */ status = false; break; } if (ascii[index] == '1') { bitstring_set_bit(bit_string, bit_number, true); bit_number++; status = true; } else if (ascii[index] == '0') { bitstring_set_bit(bit_string, bit_number, false); bit_number++; status = true; } else { /* skip non-numeric or alpha */ index++; continue; } /* next character */ index++; } } } return status; } #define CHARACTER_STRING_CAPACITY (MAX_CHARACTER_STRING_BYTES - 1) /** * Initialize a BACnet character 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, size_t length) { bool status = false; /* return value */ size_t i; /* counter */ if (char_string) { char_string->length = 0; char_string->encoding = encoding; /* save a byte at the end for NULL - note: assumes printable characters */ if (length <= CHARACTER_STRING_CAPACITY) { if (value) { for (i = 0; i < MAX_CHARACTER_STRING_BYTES; i++) { if (i < length) { char_string->value[char_string->length] = value[i]; char_string->length++; } else { char_string->value[i] = 0; } } } else { for (i = 0; i < MAX_CHARACTER_STRING_BYTES; i++) { char_string->value[i] = 0; } } status = true; } } return status; } /** * @brief Return the length of a string, within a maximum length * @note The strnlen function is non-standard and not available in * all libc implementations. This function is a workaround for that. * @details The strnlen function computes the smaller of the number * of characters in the array pointed to by s, not including any * terminating null character, or the value of the maxlen argument. * The strnlen function examines no more than maxlen bytes of the * array pointed to by s. * @param s - string to check * @param maxlen - maximum length to check * @return The strnlen function returns the number of bytes that * precede the first null character in the array pointed to by s, * if s contains a null character within the first maxlen characters; * otherwise, it returns maxlen. */ size_t characterstring_strnlen(const char *str, size_t maxlen) { const char *p = memchr(str, 0, maxlen); if (p == NULL) { return maxlen; } return (p - str); } /** * Initialize a BACnet character 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) { return characterstring_init( char_string, CHARACTER_ANSI_X34, value, value ? characterstring_strnlen(value, tmax) : 0); } /** * Initialize a BACnet character 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) { return characterstring_init( 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, const BACNET_CHARACTER_STRING *src) { if (dest && src) { return characterstring_init( dest, characterstring_encoding(src), characterstring_value(src), characterstring_length(src)); } return false; } /** * 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, const 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( const BACNET_CHARACTER_STRING *dest, const BACNET_CHARACTER_STRING *src) { size_t i; /* counter */ bool same_status = false; if (src && dest) { if ((src->encoding == dest->encoding) && (src->length == dest->length) && (src->length <= MAX_CHARACTER_STRING_BYTES)) { same_status = true; for (i = 0; i < src->length; i++) { if (src->value[i] != dest->value[i]) { same_status = false; break; } } } } else if (src) { if (src->length == 0) { same_status = true; } } else if (dest) { if (dest->length == 0) { same_status = true; } } 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( const BACNET_CHARACTER_STRING *dest, const char *src) { size_t i; /* counter */ bool same_status = false; if (src && dest) { 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; i++) { if (src[i] != dest->value[i]) { same_status = false; break; } } } } /* NULL matches an empty string in our world */ else if (src) { if (strlen(src) == 0) { same_status = true; } } else if (dest) { if (dest->length == 0) { same_status = true; } } return same_status; } /** * Returns number of UTF8 code points in a character string. * * @param dest Pointer to the string to count the UTF8 code points. * * @return Length of the character string in utf8 codepoints */ size_t characterstring_utf8_length(const BACNET_CHARACTER_STRING *str) { size_t count = 0; int i = 0; while ((i < MAX_CHARACTER_STRING_BYTES) && (str->value[i] != '\0')) { if ((str->value[i] & 0xc0) != 0x80) { count++; } i++; } return count; } /** * 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) { size_t i; /* counter */ bool status = false; /* return value */ if (char_string) { if ((length + char_string->length) <= CHARACTER_STRING_CAPACITY) { for (i = 0; i < length; i++) { char_string->value[char_string->length] = value[i]; char_string->length++; } status = true; } } return status; } /** * @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) { bool status = false; /* return value */ if (char_string) { if (length <= CHARACTER_STRING_CAPACITY) { char_string->length = length; status = true; } } return status; } /** * @brief Returns the pointer to the C-string for the given BACnet string. * * @param char_string Pointer to the character string. * * @return Pointer to a zero-terminated C-string. */ const char *characterstring_value(const BACNET_CHARACTER_STRING *char_string) { const char *value = NULL; if (char_string) { value = char_string->value; } return value; } /** * @brief Returns the length for the given BACnet string. * * @param char_string Pointer to the character string. * * @return Length of the character string, but * maximum MAX_CHARACTER_STRING_BYTES. */ size_t characterstring_length(const BACNET_CHARACTER_STRING *char_string) { size_t length = 0; if (char_string) { length = char_string->length; /* Length within bounds? */ if (length > CHARACTER_STRING_CAPACITY) { length = CHARACTER_STRING_CAPACITY; } } return length; } /** * @brief Returns the possible capacity for the given BACnet string. * * @param char_string Pointer to the character string. * * @return MAX_CHARACTER_STRING_BYTES */ size_t characterstring_capacity(const BACNET_CHARACTER_STRING *char_string) { size_t length = 0; if (char_string) { length = CHARACTER_STRING_CAPACITY; } return length; } /** * @brief Returns the character encoding for the given BACnet string. * * @param char_string Pointer to the character string. * * @return Encoding, like CHARACTER_ANSI_X34 */ uint8_t characterstring_encoding(const BACNET_CHARACTER_STRING *char_string) { uint8_t encoding = 0; if (char_string) { encoding = char_string->encoding; } return encoding; } /** * @brief Set the character encoding for the given BACnet string. * * @param char_string Pointer to the character string. * @param Encoding, like CHARACTER_ANSI_X34 * * @return true/false on error */ bool characterstring_set_encoding( BACNET_CHARACTER_STRING *char_string, uint8_t encoding) { bool status = false; if (char_string) { char_string->encoding = encoding; status = true; } return status; } /** * @brief Returns true if string is printable. * * Used to assist in the requirement that * "The set of characters used in the Object_Name shall be * restricted to printable characters." * * Printable character: a character that represents a printable * symbol as opposed to a device control character. These * include, but are not limited to, upper- and lowercase letters, * punctuation marks, and mathematical symbols. The exact set * depends upon the character set being used. In ANSI X3.4 the * printable characters are represented by single octets in the range * X'20' - X'7E'. * * @param char_string Pointer to the character string. * * @return true/false on error */ bool characterstring_printable(const BACNET_CHARACTER_STRING *char_string) { bool status = false; /* return value */ size_t i; /* counter */ size_t imax; char chr; if (char_string) { if (char_string->encoding == CHARACTER_ANSI_X34) { status = true; imax = char_string->length; if (imax > CHARACTER_STRING_CAPACITY) { imax = CHARACTER_STRING_CAPACITY; } for (i = 0; i < imax; i++) { chr = char_string->value[i]; if ((chr < 0x20) || (chr > 0x7E)) { status = false; break; } } } else { status = true; } } return status; } #if BACNET_STRING_UTF8_VALIDATION /* Basic UTF-8 manipulation routines * 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 }; /** * @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 c; size_t ab; /* An empty string is valid. */ if (length == 0) { return true; } /* Check pointer. */ if (!str) { return false; } /* Check characters. */ pend = (const unsigned char *)str + length; for (p = (const unsigned char *)str; p < pend; p++) { c = *p; /* null in middle of string */ if (c == 0) { return false; } /* ASCII character */ if (c < 128) { continue; } if ((c & 0xc0) != 0xc0) { return false; } ab = (size_t)trailingBytesForUTF8[c]; if (length < ab) { return false; } length -= ab; p++; /* Check top bits in the second byte */ if ((*p & 0xc0) != 0x80) { return false; } /* Check for overlong sequences for each different length */ switch (ab) { /* Check for xx00 000x */ case 1: if ((c & 0x3e) == 0) { return false; } continue; /* We know there aren't any more bytes to check */ /* Check for 1110 0000, xx0x xxxx */ case 2: if (c == 0xe0 && (*p & 0x20) == 0) { return false; } break; /* Check for 1111 0000, xx00 xxxx */ case 3: if (c == 0xf0 && (*p & 0x30) == 0) { return false; } break; /* Check for 1111 1000, xx00 0xxx */ case 4: if (c == 0xf8 && (*p & 0x38) == 0) { return false; } break; /* Check for leading 0xfe or 0xff, and then for 1111 1100, xx00 00xx */ case 5: if (c == 0xfe || c == 0xff || (c == 0xfc && (*p & 0x3c) == 0)) { return false; } break; default: break; } /* Check for valid bytes after the 2nd, if any; all must start 10 */ while (--ab > 0) { if ((*(++p) & 0xc0) != 0x80) { return false; } } } return true; } #else bool utf8_isvalid(const char *str, size_t length) { (void)str; (void)length; return true; } #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(const BACNET_CHARACTER_STRING *char_string) { bool valid = false; /* return value */ 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; } } } return valid; } #if BACNET_USE_OCTETSTRING /** * @brief Initialize an octet string with the given bytes or * zeros, if NULL for the value is provided. * * @param octet_string Pointer to the octet string. * @param value Pointer to the bytes to be copied to the octet * string or NULL to initialize the octet string. * @param length Count of bytes used to fill the octet string. * * @return true on success, false if the string exceeds capacity. */ bool octetstring_init( BACNET_OCTET_STRING *octet_string, const uint8_t *value, size_t length) { bool status = false; /* return value */ size_t i; /* counter */ uint8_t *pb = NULL; if (octet_string && (length <= MAX_OCTET_STRING_BYTES)) { octet_string->length = 0; if (value) { pb = octet_string->value; for (i = 0; i < MAX_OCTET_STRING_BYTES; i++) { if (i < length) { *pb = value[i]; } else { *pb = 0; } pb++; } octet_string->length = length; } else { memset(octet_string->value, 0, MAX_OCTET_STRING_BYTES); } status = true; } return status; } /** @brief Converts an null terminated ASCII Hex string to an octet string. * * @param octet_string Pointer to the octet 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) { bool status = false; /* return value */ unsigned index = 0; /* offset into buffer */ uint8_t value = 0; char hex_pair_string[3] = ""; if (octet_string && ascii_hex) { octet_string->length = 0; if (ascii_hex[0] == 0) { /* nothing to decode, so success! */ status = true; } else { while (ascii_hex[index] != 0) { if (!isalnum((int)ascii_hex[index])) { /* skip non-numeric or alpha */ index++; continue; } if (ascii_hex[index + 1] == 0) { /* not a hex pair */ status = false; break; } hex_pair_string[0] = ascii_hex[index]; hex_pair_string[1] = ascii_hex[index + 1]; value = (uint8_t)strtol(hex_pair_string, NULL, 16); if (octet_string->length <= MAX_OCTET_STRING_BYTES) { octet_string->value[octet_string->length] = value; octet_string->length++; /* at least one pair was decoded */ status = true; } else { /* too long */ status = false; break; } /* set up for next pair */ index += 2; } } } return status; } /** * Copy an octet string from source to destination. * * @param dest Pointer to the destination octet string. * @param src Pointer to the source octet string. * * @return true on success, false otherwise. */ bool octetstring_copy(BACNET_OCTET_STRING *dest, const BACNET_OCTET_STRING *src) { return octetstring_init( dest, octetstring_value((BACNET_OCTET_STRING *)src), octetstring_length(src)); } /** * @brief Copy bytes from the octet string to a byte buffer. * * @param dest Pointer to the byte buffer. * @param length Bytes to be copied from the * octet string to the buffer. * @param src Pointer to the octet 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, const BACNET_OCTET_STRING *src) { size_t bytes_copied = 0; size_t i; /* counter */ if (src && dest) { if (length <= src->length) { for (i = 0; i < src->length; i++) { dest[i] = src->value[i]; } bytes_copied = src->length; } } return bytes_copied; } /** * @brief Append bytes to the end of the octet string. * * @param octet_string Pointer to the octet 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, const uint8_t *value, size_t length) { size_t i; /* counter */ bool status = false; /* return value */ if (octet_string) { if ((length + octet_string->length) <= MAX_OCTET_STRING_BYTES) { for (i = 0; i < length; i++) { octet_string->value[octet_string->length] = value[i]; octet_string->length++; } status = true; } } return status; } /** * @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 octet string. * @param length New length the octet string is truncated to. * * @return tur on success, false otherwise. */ bool octetstring_truncate(BACNET_OCTET_STRING *octet_string, size_t length) { bool status = false; /* return value */ if (octet_string) { if (length <= MAX_OCTET_STRING_BYTES) { octet_string->length = length; status = true; } } return status; } /** * @brief Returns a pointer to the value (data) of * the given octet string. * * @param octet_string Pointer to the octet 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; if (octet_string) { value = octet_string->value; } return value; } /** * @brief Returns the length in bytes of * the given octet string. * * @param octet_string Pointer to the octet string. * * @return Length in bytes. Returns always 0 on error. */ size_t octetstring_length(const BACNET_OCTET_STRING *octet_string) { size_t length = 0; if (octet_string) { length = octet_string->length; /* Force length to be within bounds. */ if (length > MAX_OCTET_STRING_BYTES) { length = MAX_OCTET_STRING_BYTES; } } return length; } /** * @brief Returns the maximum capacity of an octet string. * * @param octet_string Pointer to the octet string. * * @return Capacity in bytes. Returns always 0 on error. */ size_t octetstring_capacity(const BACNET_OCTET_STRING *octet_string) { size_t length = 0; if (octet_string) { length = MAX_OCTET_STRING_BYTES; } return length; } /** * @brief Returns true if the same length and contents. * * @param octet_string1 Pointer to the first octet string. * @param octet_string2 Pointer to the second octet string. * * @return true if the octet strings are the same, false otherwise. */ bool octetstring_value_same( const BACNET_OCTET_STRING *octet_string1, const BACNET_OCTET_STRING *octet_string2) { size_t i = 0; /* loop counter */ if (octet_string1 && octet_string2) { if ((octet_string1->length == octet_string2->length) && (octet_string1->length <= MAX_OCTET_STRING_BYTES)) { for (i = 0; i < octet_string1->length; i++) { if (octet_string1->value[i] != octet_string2->value[i]) { return false; } } return true; } } return false; } #endif