Feature/app data buffer check (#79)
* Added comments and buffer overflow checks * Removed backslashs from C-code.
This commit is contained in:
@@ -84,11 +84,23 @@ static struct Address_Cache_Entry {
|
||||
#define BAC_ADDR_SHORT_TIME BAC_ADDR_SECS_1HOUR
|
||||
#define BAC_ADDR_FOREVER 0xFFFFFFFF /* Permenant entry */
|
||||
|
||||
/**
|
||||
* @brief Set the index of the first (top) address being protected.
|
||||
*
|
||||
* @param top_protected_entry_index top protected index [0..n-1]
|
||||
*/
|
||||
void address_protected_entry_index_set(uint32_t top_protected_entry_index)
|
||||
{
|
||||
Top_Protected_Entry = top_protected_entry_index;
|
||||
if (top_protected_entry_index <= (MAX_ADDRESS_CACHE - 1)) {
|
||||
Top_Protected_Entry = top_protected_entry_index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the address of our own device.
|
||||
*
|
||||
* @param own_id Own device id
|
||||
*/
|
||||
void address_own_device_id_set(uint32_t own_id)
|
||||
{
|
||||
Own_Device_ID = own_id;
|
||||
@@ -182,9 +194,8 @@ void address_remove_device(uint32_t device_id)
|
||||
* entry available to free up. Does not check for free entries as it is
|
||||
* assumed we are calling this due to the lack of those.
|
||||
*
|
||||
* @return Candidate for deletation.
|
||||
* @return Pointer to the entry that has been removed or NULL.
|
||||
*/
|
||||
|
||||
static struct Address_Cache_Entry *address_remove_oldest(void)
|
||||
{
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
@@ -383,11 +394,10 @@ static void address_file_init(const char *pFilename)
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Clear down the cache and make sure the full complement of entries are *
|
||||
* available. Assume no persistance of memory. *
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* Clear down the cache and make sure the full complement of entries are
|
||||
* available. Assume no persistance of memory.
|
||||
*/
|
||||
void address_init(void)
|
||||
{
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
@@ -405,14 +415,13 @@ void address_init(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Clear down the cache of any non bound, expired or reserved entries. *
|
||||
* Leave static and unexpired bound entries alone. For use where the cache *
|
||||
* is held in persistant memory which can survive a reset or power cycle. *
|
||||
* This reduces the network traffic on restarts as the cache will have much *
|
||||
* of its entries intact. *
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* Clear down the cache of any non bound, expired or reserved entries.
|
||||
* Leave static and unexpired bound entries alone. For use where the cache
|
||||
* is held in persistant memory which can survive a reset or power cycle.
|
||||
* This reduces the network traffic on restarts as the cache will have much
|
||||
* of its entries intact.
|
||||
*/
|
||||
void address_init_partial(void)
|
||||
{
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
@@ -441,13 +450,16 @@ void address_init_partial(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Set the TTL info for the given device entry. If it is a bound entry we *
|
||||
* set it to static or normal and can change the TTL. If it is unbound we *
|
||||
* can only set the TTL. This is done as a seperate function at the moment *
|
||||
* to avoid breaking the current API. *
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* Set the TTL info for the given device entry. If it is a bound entry we
|
||||
* set it to static or normal and can change the TTL. If it is unbound we
|
||||
* can only set the TTL. This is done as a seperate function at the moment
|
||||
* to avoid breaking the current API.
|
||||
*
|
||||
* @param device_id Device-Id
|
||||
* @param TimeOut Timeout in seconds.
|
||||
* @param StaticFlag Flag indicating if being a static address.
|
||||
*/
|
||||
void address_set_device_TTL(
|
||||
uint32_t device_id, uint32_t TimeOut, bool StaticFlag)
|
||||
{
|
||||
@@ -476,6 +488,13 @@ void address_set_device_TTL(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cached address for the given device-id
|
||||
*
|
||||
* @param device_id Device-Id
|
||||
* @param max_apdu Pointer to a variable, taking the maximum APDU size.
|
||||
* @param src Pointer to address structure for return.
|
||||
*/
|
||||
bool address_get_by_device(
|
||||
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src)
|
||||
{
|
||||
@@ -500,8 +519,14 @@ bool address_get_by_device(
|
||||
return found;
|
||||
}
|
||||
|
||||
/* find a device id from a given MAC address */
|
||||
|
||||
/**
|
||||
* Find a device id from a given MAC address.
|
||||
*
|
||||
* @param src Pointer to address structure to search for.
|
||||
* @param device_id Pointer to the device id variable for return.
|
||||
*
|
||||
* @return true/false
|
||||
*/
|
||||
bool address_get_device_id(BACNET_ADDRESS *src, uint32_t *device_id)
|
||||
{
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
@@ -525,6 +550,13 @@ bool address_get_device_id(BACNET_ADDRESS *src, uint32_t *device_id)
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a device using the given id, max_apdu and address.
|
||||
*
|
||||
* @param device_id Pointer to the device id variable for return.
|
||||
* @param max_apdu Maximum APDU size.
|
||||
* @param src Pointer to address structure to add.
|
||||
*/
|
||||
void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
|
||||
{
|
||||
bool found = false; /* return value */
|
||||
@@ -543,6 +575,7 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
|
||||
/* existing device or bind request outstanding - update address */
|
||||
pMatch = Address_Cache;
|
||||
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
/* Device already in the list, then update the values. */
|
||||
if (((pMatch->Flags & BAC_ADDR_IN_USE) != 0) &&
|
||||
(pMatch->device_id == device_id)) {
|
||||
bacnet_address_copy(&pMatch->address, src);
|
||||
@@ -572,7 +605,7 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
|
||||
pMatch++;
|
||||
}
|
||||
|
||||
/* new device - add to cache if there is room */
|
||||
/* New device - add to cache if there is room. */
|
||||
if (!found) {
|
||||
pMatch = Address_Cache;
|
||||
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
@@ -591,7 +624,7 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
|
||||
}
|
||||
}
|
||||
|
||||
/* See if we can squeeze it in */
|
||||
/* If adding has failed, see if we can squeeze it in by removed the oldest entry. */
|
||||
if (!found) {
|
||||
pMatch = address_remove_oldest();
|
||||
if (pMatch != NULL) {
|
||||
@@ -606,8 +639,19 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
|
||||
return;
|
||||
}
|
||||
|
||||
/* returns true if device is already bound */
|
||||
/* also returns the address and max apdu if already bound */
|
||||
/**
|
||||
* Check if the device is in the list. If yes, return the values.
|
||||
* Otherwise add the device to the list.
|
||||
* Returns true if device is already bound.
|
||||
* Also returns the address and max apdu if already bound.
|
||||
*
|
||||
* @param device_id Pointer to the device id variable for return.
|
||||
* @param device_ttl Pointer to a variable taking the time to live in seconds.
|
||||
* @param max_apdu Max APDU size of the device.
|
||||
* @param src Pointer to the BACnet address.
|
||||
*
|
||||
* @return true if device is already bound
|
||||
*/
|
||||
bool address_device_bind_request(uint32_t device_id,
|
||||
uint32_t *device_ttl,
|
||||
unsigned *max_apdu,
|
||||
@@ -675,14 +719,31 @@ bool address_device_bind_request(uint32_t device_id,
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* returns true if device is already bound */
|
||||
/* also returns the address and max apdu if already bound */
|
||||
/**
|
||||
* Check if the device is in the list. If yes, return the values.
|
||||
* Otherwise add the device to the list.
|
||||
* Returns true if device is already bound.
|
||||
* Also returns the address and max apdu if already bound.
|
||||
*
|
||||
* @param device_id Pointer to the device id variable for return.
|
||||
* @param max_apdu Max APDU size of the device.
|
||||
* @param src Pointer to the BACnet address.
|
||||
*
|
||||
* @return true if device is already bound
|
||||
*/
|
||||
bool address_bind_request(
|
||||
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src)
|
||||
{
|
||||
return address_device_bind_request(device_id, NULL, max_apdu, src);
|
||||
}
|
||||
|
||||
/**
|
||||
* For an existing device add a binding.
|
||||
*
|
||||
* @param device_id Pointer to the device id variable for return.
|
||||
* @param max_apdu Max APDU size of the device.
|
||||
* @param src Pointer to the BACnet address.
|
||||
*/
|
||||
void address_add_binding(
|
||||
uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
|
||||
{
|
||||
@@ -709,6 +770,17 @@ void address_add_binding(
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the device information from the given index in the table.
|
||||
*
|
||||
* @param index Table index [0..MAX_ADDRESS_CACHE-1]
|
||||
* @param device_id Pointer to the variable taking the device id.
|
||||
* @param device_ttl Pointer to the variable taking the Time To Life for the device.
|
||||
* @param max_apdu Pointer to the variable taking the max APDU size of the device.
|
||||
* @param src Pointer to the BACnet address.
|
||||
*
|
||||
* @return true/false
|
||||
*/
|
||||
bool address_device_get_by_index(unsigned index,
|
||||
uint32_t *device_id,
|
||||
uint32_t *device_ttl,
|
||||
@@ -741,6 +813,16 @@ bool address_device_get_by_index(unsigned index,
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the device information from the given index in the table.
|
||||
*
|
||||
* @param index Table index [0..MAX_ADDRESS_CACHE-1]
|
||||
* @param device_id Pointer to the variable taking the device id.
|
||||
* @param max_apdu Pointer to the variable taking the max APDU size of the device.
|
||||
* @param src Pointer to the BACnet address.
|
||||
*
|
||||
* @return true/false
|
||||
*/
|
||||
bool address_get_by_index(unsigned index,
|
||||
uint32_t *device_id,
|
||||
unsigned *max_apdu,
|
||||
@@ -749,6 +831,11 @@ bool address_get_by_index(unsigned index,
|
||||
return address_device_get_by_index(index, device_id, NULL, max_apdu, src);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of cached addresses.
|
||||
*
|
||||
* @return A value between zero and MAX_ADDRESS_CACHE.
|
||||
*/
|
||||
unsigned address_count(void)
|
||||
{
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
@@ -768,43 +855,55 @@ unsigned address_count(void)
|
||||
return count;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Build a list of the current bindings for the device address binding *
|
||||
* property. *
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* Build a list of the current bindings for the device address binding
|
||||
* property. Basically encode the address list to be send out.
|
||||
*
|
||||
* @param apdu Pointer to the APDU
|
||||
* @param apdu_len Remaining buffer size.
|
||||
*
|
||||
* @return Count of encoded bytes.
|
||||
*/
|
||||
int address_list_encode(uint8_t *apdu, unsigned apdu_len)
|
||||
{
|
||||
int iLen = 0;
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
BACNET_OCTET_STRING MAC_Address;
|
||||
|
||||
/* FIXME: I really shouild check the length remaining here but it is
|
||||
fairly pointless until we have the true length remaining in
|
||||
the packet to work with as at the moment it is just MAX_APDU */
|
||||
(void)apdu_len;
|
||||
/* look for matching address */
|
||||
/* Look for matching address. */
|
||||
pMatch = Address_Cache;
|
||||
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) ==
|
||||
BAC_ADDR_IN_USE) {
|
||||
iLen += encode_application_object_id(
|
||||
&apdu[iLen], OBJECT_DEVICE, pMatch->device_id);
|
||||
iLen +=
|
||||
encode_application_unsigned(&apdu[iLen], pMatch->address.net);
|
||||
&apdu[iLen], OBJECT_DEVICE, pMatch->device_id);
|
||||
iLen += encode_application_unsigned(&apdu[iLen], pMatch->address.net);
|
||||
if ((unsigned)iLen >= apdu_len) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* pick the appropriate type of entry from the cache */
|
||||
|
||||
if (pMatch->address.len != 0) {
|
||||
/* BAC */
|
||||
if ((unsigned)(iLen + pMatch->address.len) >= apdu_len) {
|
||||
break;
|
||||
}
|
||||
octetstring_init(
|
||||
&MAC_Address, pMatch->address.adr, pMatch->address.len);
|
||||
iLen +=
|
||||
encode_application_octet_string(&apdu[iLen], &MAC_Address);
|
||||
iLen += encode_application_octet_string(&apdu[iLen], &MAC_Address);
|
||||
} else {
|
||||
/* MAC*/
|
||||
if ((unsigned)(iLen + pMatch->address.mac_len) >= apdu_len) {
|
||||
break;
|
||||
}
|
||||
octetstring_init(
|
||||
&MAC_Address, pMatch->address.mac, pMatch->address.mac_len);
|
||||
iLen +=
|
||||
encode_application_octet_string(&apdu[iLen], &MAC_Address);
|
||||
iLen += encode_application_octet_string(&apdu[iLen], &MAC_Address);
|
||||
}
|
||||
/* Any space left? */
|
||||
if ((unsigned)iLen >= apdu_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pMatch++;
|
||||
@@ -813,29 +912,33 @@ int address_list_encode(uint8_t *apdu, unsigned apdu_len)
|
||||
return (iLen);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Build a list of the current bindings for the device address binding *
|
||||
* property as required for the ReadsRange functionality. *
|
||||
* We assume we only get called for "Read All" or "By Position" requests. *
|
||||
* *
|
||||
* We need to treat the address cache as a contiguous array but in reality *
|
||||
* it could be sparsely populated. We can get the count but we can only *
|
||||
* extract entries by doing a linear scan starting from the first entry in *
|
||||
* the cache and picking them off one by one. *
|
||||
* *
|
||||
* We do assume the list cannot change whilst we are accessing it so would *
|
||||
* not be multithread safe if there are other tasks that change the cache. *
|
||||
* *
|
||||
/**
|
||||
* Build a list of the current bindings for the device address binding
|
||||
* property as required for the ReadsRange functionality.
|
||||
* We assume we only get called for "Read All" or "By Position" requests.
|
||||
*
|
||||
* We need to treat the address cache as a contiguous array but in reality
|
||||
* it could be sparsely populated. We can get the count but we can only
|
||||
* extract entries by doing a linear scan starting from the first entry in
|
||||
* the cache and picking them off one by one.
|
||||
*
|
||||
* We do assume the list cannot change whilst we are accessing it so would
|
||||
* not be multithread safe if there are other tasks that change the cache.
|
||||
*
|
||||
* We take the simple approach here to filling the buffer by taking a max *
|
||||
* size for a single entry and then stopping if there is less than that *
|
||||
* left in the buffer. You could build each entry in a seperate buffer and *
|
||||
* determine the exact length before copying but this is time consuming, *
|
||||
* requires more memory and would probably only let you sqeeeze one more *
|
||||
* entry in on occasion. The value is calculated as 5 bytes for the device *
|
||||
* ID + 3 bytes for the network number and nine bytes for the MAC address *
|
||||
* oct string to give 17 bytes (the minimum possible is 5 + 2 + 3 = 10). *
|
||||
****************************************************************************/
|
||||
|
||||
* size for a single entry and then stopping if there is less than that
|
||||
* left in the buffer. You could build each entry in a seperate buffer and
|
||||
* determine the exact length before copying but this is time consuming,
|
||||
* requires more memory and would probably only let you sqeeeze one more
|
||||
* entry in on occasion. The value is calculated as 5 bytes for the device
|
||||
* ID + 3 bytes for the network number and nine bytes for the MAC address
|
||||
* oct string to give 17 bytes (the minimum possible is 5 + 2 + 3 = 10).
|
||||
*
|
||||
* @param apdu Pointer to the encode buffer.
|
||||
* @param pRequest Pointer to the read_range request structure.
|
||||
*
|
||||
* @return Bytes encoded.
|
||||
*/
|
||||
#define ACACHE_MAX_ENC 17 /* Maximum size of encoded cache entry, see above */
|
||||
|
||||
int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
@@ -851,6 +954,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
uint32_t uiTarget = 0; /* Last entry we are required to encode */
|
||||
uint32_t uiRemaining = 0; /* Amount of unused space in packet */
|
||||
|
||||
if ((!pRequest) || (!apdu)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialise result flags to all false */
|
||||
bitstring_init(&pRequest->ResultFlags);
|
||||
bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, false);
|
||||
@@ -861,7 +968,7 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
|
||||
pRequest->ItemCount = 0; /* Start out with nothing */
|
||||
uiTotal = address_count(); /* What do we have to work with here ? */
|
||||
if (uiTotal == 0) { /* Bail out now if nowt */
|
||||
if (uiTotal == 0) { /* Bail out now if not. */
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -921,6 +1028,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
while ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) !=
|
||||
BAC_ADDR_IN_USE) { /* Find first bound entry */
|
||||
pMatch++;
|
||||
/* Shall not happen as the count has been checked first. */
|
||||
if (pMatch > &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
return(0); /* Issue with the table. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Seek to start position */
|
||||
@@ -932,6 +1043,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
} else {
|
||||
pMatch++;
|
||||
}
|
||||
/* Shall not happen as the count has been checked first. */
|
||||
if (pMatch > &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
return(0); /* Issue with the table. */
|
||||
}
|
||||
}
|
||||
|
||||
uiFirst = uiIndex; /* Record where we started from */
|
||||
@@ -976,6 +1091,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
while ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) !=
|
||||
BAC_ADDR_IN_USE) { /* Find next bound entry */
|
||||
pMatch++;
|
||||
/* Can normally not happen. */
|
||||
if (pMatch > &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
return(0); /* Issue with the table. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,15 +1110,16 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
return (iLen);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Scan the cache and eliminate any expired entries. Should be called *
|
||||
* periodically to ensure the cache is managed correctly. If this function *
|
||||
* is never called at all the whole cache is effectivly rendered static and *
|
||||
* entries never expire unless explictely deleted. *
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* Scan the cache and eliminate any expired entries. Should be called
|
||||
* periodically to ensure the cache is managed correctly. If this function
|
||||
* is never called at all the whole cache is effectivly rendered static and
|
||||
* entries never expire unless explictely deleted.
|
||||
*
|
||||
* @param uSeconds Approximate number of seconds since last call to this function
|
||||
*/
|
||||
void address_cache_timer(uint16_t uSeconds)
|
||||
{ /* Approximate number of seconds since last call to this function */
|
||||
{
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
|
||||
pMatch = Address_Cache;
|
||||
|
||||
@@ -422,7 +422,7 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
if (wp_data == NULL) {
|
||||
return false;
|
||||
}
|
||||
if ((wp_data->application_data == NULL) || \
|
||||
if ((wp_data->application_data == NULL) ||
|
||||
(wp_data->application_data_len == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -384,7 +384,7 @@ int CharacterString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
if (rpdata == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if ((rpdata->application_data == NULL) || \
|
||||
if ((rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -488,7 +488,7 @@ bool CharacterString_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
if (wp_data == NULL) {
|
||||
return false;
|
||||
}
|
||||
if ((wp_data->application_data == NULL) || \
|
||||
if ((wp_data->application_data == NULL) ||
|
||||
(wp_data->application_data_len == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+236
-211
@@ -182,6 +182,7 @@ void handler_read_property_multiple(uint8_t *service_request,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data)
|
||||
{
|
||||
bool berror = false;
|
||||
int len = 0;
|
||||
uint16_t copy_len = 0;
|
||||
uint16_t decode_len = 0;
|
||||
@@ -194,234 +195,258 @@ void handler_read_property_multiple(uint8_t *service_request,
|
||||
int npdu_len = 0;
|
||||
int error = 0;
|
||||
|
||||
/* jps_debug - see if we are utilizing all the buffer */
|
||||
/* memset(&Handler_Transmit_Buffer[0], 0xff,
|
||||
* sizeof(Handler_Transmit_Buffer)); */
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
npdu_len = npdu_encode_pdu(
|
||||
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
|
||||
if (service_data->segmented_message) {
|
||||
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n");
|
||||
#endif
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
/* decode apdu request & encode apdu reply
|
||||
encode complex ack, invoke id, service choice */
|
||||
apdu_len = rpm_ack_encode_apdu_init(
|
||||
&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id);
|
||||
for (;;) {
|
||||
/* Start by looking for an object ID */
|
||||
len = rpm_decode_object_id(
|
||||
&service_request[decode_len], service_len - decode_len, &rpmdata);
|
||||
if (len >= 0) {
|
||||
/* Got one so skip to next stage */
|
||||
decode_len += len;
|
||||
} else {
|
||||
/* bad encoding - skip to error/reject/abort handling */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Bad Encoding.\n");
|
||||
#endif
|
||||
error = len;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
if (service_data && (service_len > 0)) {
|
||||
/* jps_debug - see if we are utilizing all the buffer */
|
||||
/* memset(&Handler_Transmit_Buffer[0], 0xff,
|
||||
* sizeof(Handler_Transmit_Buffer)); */
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
npdu_len = npdu_encode_pdu(
|
||||
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
|
||||
|
||||
/* Test for case of indefinite Device object instance */
|
||||
if ((rpmdata.object_type == OBJECT_DEVICE) &&
|
||||
(rpmdata.object_instance == BACNET_MAX_INSTANCE)) {
|
||||
rpmdata.object_instance = Device_Object_Instance_Number();
|
||||
}
|
||||
|
||||
/* Stick this object id into the reply - if it will fit */
|
||||
len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata);
|
||||
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0],
|
||||
apdu_len, len, MAX_APDU);
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Response too big!\r\n");
|
||||
#endif
|
||||
if (service_data->segmented_message) {
|
||||
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n");
|
||||
#endif
|
||||
} else {
|
||||
/* decode apdu request & encode apdu reply
|
||||
encode complex ack, invoke id, service choice */
|
||||
apdu_len = rpm_ack_encode_apdu_init(
|
||||
&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id);
|
||||
|
||||
apdu_len += copy_len;
|
||||
/* do each property of this object of the RPM request */
|
||||
for (;;) {
|
||||
/* Fetch a property */
|
||||
len = rpm_decode_object_property(&service_request[decode_len],
|
||||
service_len - decode_len, &rpmdata);
|
||||
if (len < 0) {
|
||||
/* bad encoding - skip to error/reject/abort handling */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Bad Encoding.\n");
|
||||
#endif
|
||||
error = len;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
decode_len += len;
|
||||
/* handle the special properties */
|
||||
if ((rpmdata.object_property == PROP_ALL) ||
|
||||
(rpmdata.object_property == PROP_REQUIRED) ||
|
||||
(rpmdata.object_property == PROP_OPTIONAL)) {
|
||||
struct special_property_list_t property_list;
|
||||
unsigned property_count = 0;
|
||||
unsigned index = 0;
|
||||
BACNET_PROPERTY_ID special_object_property;
|
||||
|
||||
if (rpmdata.array_index != BACNET_ARRAY_ALL) {
|
||||
/* No array index options for this special property.
|
||||
Encode error for this object property response */
|
||||
len = rpm_ack_encode_apdu_object_property(&Temp_Buf[0],
|
||||
rpmdata.object_property, rpmdata.array_index);
|
||||
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
|
||||
&Temp_Buf[0], apdu_len, len, MAX_APDU);
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(
|
||||
stderr, "RPM: Too full to encode property!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
apdu_len += len;
|
||||
len = rpm_ack_encode_apdu_object_property_error(
|
||||
&Temp_Buf[0], ERROR_CLASS_PROPERTY,
|
||||
ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY);
|
||||
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
|
||||
&Temp_Buf[0], apdu_len, len, MAX_APDU);
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Too full to encode error!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
apdu_len += len;
|
||||
for (;;) {
|
||||
/* Start by looking for an object ID */
|
||||
len = rpm_decode_object_id(
|
||||
&service_request[decode_len], service_len - decode_len, &rpmdata);
|
||||
if (len >= 0) {
|
||||
/* Got one so skip to next stage */
|
||||
decode_len += len;
|
||||
} else {
|
||||
special_object_property = rpmdata.object_property;
|
||||
Device_Objects_Property_List(rpmdata.object_type,
|
||||
rpmdata.object_instance, &property_list);
|
||||
property_count = RPM_Object_Property_Count(
|
||||
&property_list, special_object_property);
|
||||
if (property_count == 0) {
|
||||
/* this only happens with the OPTIONAL property */
|
||||
/* 135-2016bl-2. Clarify ReadPropertyMultiple
|
||||
response on OPTIONAL when empty. */
|
||||
/* If no optional properties are supported then
|
||||
an empty 'List of Results' shall be returned
|
||||
for the specified property.*/
|
||||
} else {
|
||||
for (index = 0; index < property_count; index++) {
|
||||
rpmdata.object_property = RPM_Object_Property(
|
||||
&property_list, special_object_property, index);
|
||||
len = RPM_Encode_Property(
|
||||
&Handler_Transmit_Buffer[npdu_len],
|
||||
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
/* bad encoding - skip to error/reject/abort handling */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Bad Encoding.\n");
|
||||
#endif
|
||||
error = len;
|
||||
berror = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Test for case of indefinite Device object instance */
|
||||
if ((rpmdata.object_type == OBJECT_DEVICE) &&
|
||||
(rpmdata.object_instance == BACNET_MAX_INSTANCE)) {
|
||||
rpmdata.object_instance = Device_Object_Instance_Number();
|
||||
}
|
||||
|
||||
/* Stick this object id into the reply - if it will fit */
|
||||
len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata);
|
||||
copy_len =
|
||||
memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0],
|
||||
apdu_len, len, MAX_APDU);
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Response too big!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
berror = true;
|
||||
break;
|
||||
}
|
||||
|
||||
apdu_len += copy_len;
|
||||
/* do each property of this object of the RPM request */
|
||||
for (;;) {
|
||||
/* Fetch a property */
|
||||
len = rpm_decode_object_property(&service_request[decode_len],
|
||||
service_len - decode_len, &rpmdata);
|
||||
if (len < 0) {
|
||||
/* bad encoding - skip to error/reject/abort handling */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Bad Encoding.\n");
|
||||
#endif
|
||||
error = len;
|
||||
berror = true;
|
||||
break; // The berror flag ensures that both loops will be broken!
|
||||
}
|
||||
decode_len += len;
|
||||
/* handle the special properties */
|
||||
if ((rpmdata.object_property == PROP_ALL) ||
|
||||
(rpmdata.object_property == PROP_REQUIRED) ||
|
||||
(rpmdata.object_property == PROP_OPTIONAL)) {
|
||||
struct special_property_list_t property_list;
|
||||
unsigned property_count = 0;
|
||||
unsigned index = 0;
|
||||
BACNET_PROPERTY_ID special_object_property;
|
||||
|
||||
if (rpmdata.array_index != BACNET_ARRAY_ALL) {
|
||||
|
||||
/* No array index options for this special property.
|
||||
Encode error for this object property response */
|
||||
len = rpm_ack_encode_apdu_object_property(&Temp_Buf[0],
|
||||
rpmdata.object_property, rpmdata.array_index);
|
||||
|
||||
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
|
||||
&Temp_Buf[0], apdu_len, len, MAX_APDU);
|
||||
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(
|
||||
stderr, "RPM: Too full for property!\r\n");
|
||||
#endif
|
||||
error = len;
|
||||
goto RPM_FAILURE;
|
||||
stderr, "RPM: Too full to encode property!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
berror = true;
|
||||
break; // The berror flag ensures that both loops will be broken!
|
||||
}
|
||||
|
||||
apdu_len += len;
|
||||
len = rpm_ack_encode_apdu_object_property_error(
|
||||
&Temp_Buf[0], ERROR_CLASS_PROPERTY,
|
||||
ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY);
|
||||
|
||||
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
|
||||
&Temp_Buf[0], apdu_len, len, MAX_APDU);
|
||||
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Too full to encode error!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
berror = true;
|
||||
break; // The berror flag ensures that both loops will be broken!
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
special_object_property = rpmdata.object_property;
|
||||
Device_Objects_Property_List(rpmdata.object_type,
|
||||
rpmdata.object_instance, &property_list);
|
||||
property_count = RPM_Object_Property_Count(
|
||||
&property_list, special_object_property);
|
||||
|
||||
if (property_count == 0) {
|
||||
/* This only happens with the OPTIONAL property */
|
||||
/* 135-2016bl-2. Clarify ReadPropertyMultiple
|
||||
response on OPTIONAL when empty. */
|
||||
/* If no optional properties are supported then
|
||||
an empty 'List of Results' shall be returned
|
||||
for the specified property.*/
|
||||
} else {
|
||||
for (index = 0; index < property_count; index++) {
|
||||
rpmdata.object_property = RPM_Object_Property(
|
||||
&property_list, special_object_property, index);
|
||||
len = RPM_Encode_Property(
|
||||
&Handler_Transmit_Buffer[npdu_len],
|
||||
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(
|
||||
stderr, "RPM: Too full for property!\r\n");
|
||||
#endif
|
||||
error = len;
|
||||
berror = true;
|
||||
break; // The berror flag ensures that both loops will be broken!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* handle an individual property */
|
||||
len = RPM_Encode_Property(&Handler_Transmit_Buffer[npdu_len],
|
||||
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(
|
||||
stderr, "RPM: Too full for individual property!\r\n");
|
||||
#endif
|
||||
error = len;
|
||||
berror = true;
|
||||
break; // The berror flag ensures that both loops will be broken!
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_is_closing_tag_number(&service_request[decode_len], 1)) {
|
||||
/* Reached end of property list so cap the result list */
|
||||
decode_len++;
|
||||
len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]);
|
||||
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
|
||||
&Temp_Buf[0], apdu_len, len, MAX_APDU);
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Too full to encode object end!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
berror = true;
|
||||
break; // The berror flag ensures that both loops will be broken!
|
||||
} else {
|
||||
apdu_len += copy_len;
|
||||
}
|
||||
break; /* finished with this property list */
|
||||
}
|
||||
} // for(;;)
|
||||
if (berror) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* handle an individual property */
|
||||
len = RPM_Encode_Property(&Handler_Transmit_Buffer[npdu_len],
|
||||
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(
|
||||
stderr, "RPM: Too full for individual property!\r\n");
|
||||
#endif
|
||||
error = len;
|
||||
goto RPM_FAILURE;
|
||||
if (decode_len >= service_len) {
|
||||
/* Reached the end so finish up */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (decode_is_closing_tag_number(&service_request[decode_len], 1)) {
|
||||
/* Reached end of property list so cap the result list */
|
||||
decode_len++;
|
||||
len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]);
|
||||
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
|
||||
&Temp_Buf[0], apdu_len, len, MAX_APDU);
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Too full to encode object end!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
} // for(;;)
|
||||
|
||||
/* If not having an error so far, check the remaining space. */
|
||||
if (!berror) {
|
||||
if (apdu_len > service_data->max_resp) {
|
||||
/* too big for the sender - send an abort */
|
||||
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
goto RPM_FAILURE;
|
||||
} else {
|
||||
apdu_len += copy_len;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Message too large. Sending Abort!\n");
|
||||
#endif
|
||||
}
|
||||
break; /* finished with this property list */
|
||||
}
|
||||
}
|
||||
if (decode_len >= service_len) {
|
||||
/* Reached the end so finish up */
|
||||
break;
|
||||
|
||||
/* Error fallback. */
|
||||
if (error) {
|
||||
if (error == BACNET_STATUS_ABORT) {
|
||||
apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
abort_convert_error_code(rpmdata.error_code), true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Sending Abort!\n");
|
||||
#endif
|
||||
} else if (error == BACNET_STATUS_ERROR) {
|
||||
apdu_len = bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
|
||||
rpmdata.error_class, rpmdata.error_code);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Sending Error!\n");
|
||||
#endif
|
||||
} else if (error == BACNET_STATUS_REJECT) {
|
||||
apdu_len = reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
reject_convert_error_code(rpmdata.error_code));
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Sending Reject!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (apdu_len > service_data->max_resp) {
|
||||
/* too big for the sender - send an abort */
|
||||
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Message too large. Sending Abort!\n");
|
||||
#endif
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
|
||||
RPM_FAILURE:
|
||||
if (error) {
|
||||
if (error == BACNET_STATUS_ABORT) {
|
||||
apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
abort_convert_error_code(rpmdata.error_code), true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Sending Abort!\n");
|
||||
#endif
|
||||
} else if (error == BACNET_STATUS_ERROR) {
|
||||
apdu_len = bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
|
||||
rpmdata.error_class, rpmdata.error_code);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Sending Error!\n");
|
||||
#endif
|
||||
} else if (error == BACNET_STATUS_REJECT) {
|
||||
apdu_len = reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
reject_convert_error_code(rpmdata.error_code));
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Sending Reject!\n");
|
||||
#endif
|
||||
pdu_len = apdu_len + npdu_len;
|
||||
bytes_sent = datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Failed to send PDU (%s)!\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
pdu_len = apdu_len + npdu_len;
|
||||
bytes_sent = datalink_send_pdu(
|
||||
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Failed to send PDU (%s)!\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ void handler_write_property(uint8_t *service_request,
|
||||
|
||||
/* Send PDU */
|
||||
pdu_len += len;
|
||||
bytes_sent = datalink_send_pdu( \
|
||||
bytes_sent = datalink_send_pdu(
|
||||
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
|
||||
+192
-82
@@ -56,21 +56,33 @@
|
||||
/* Generic node routines */
|
||||
/******************************************************************** */
|
||||
|
||||
/* grab memory for a node */
|
||||
/** Grab memory for a node (Keylist_Node).
|
||||
*
|
||||
* @return Pointer to the allocated memory or
|
||||
* NULL under an Out Of Memory situation.
|
||||
*/
|
||||
static struct Keylist_Node *NodeCreate(void)
|
||||
{
|
||||
return calloc(1, sizeof(struct Keylist_Node));
|
||||
}
|
||||
|
||||
/* grab memory for a list */
|
||||
/** Grab memory for a list (Keylist).
|
||||
*
|
||||
* @return Pointer to the allocated memory or
|
||||
* NULL under an Out Of Memory situation.
|
||||
*/
|
||||
static struct Keylist *KeylistCreate(void)
|
||||
{
|
||||
return calloc(1, sizeof(struct Keylist));
|
||||
}
|
||||
|
||||
/* check to see if the array is big enough for an addition */
|
||||
/* or is too big when we are deleting and we can shrink */
|
||||
/* returns TRUE if success, FALSE if failed */
|
||||
/** Check to see if the array is big enough for an addition
|
||||
* or is too big when we are deleting and we can shrink.
|
||||
*
|
||||
* @param list Pointer to the list to be tested.
|
||||
*
|
||||
* @return Returns TRUE if success, FALSE if failed
|
||||
*/
|
||||
static int CheckArraySize(OS_Keylist list)
|
||||
{
|
||||
int new_size = 0; /* set it up so that no size change is the default */
|
||||
@@ -112,13 +124,20 @@ static int CheckArraySize(OS_Keylist list)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* find the index of the key that we are looking for */
|
||||
/* since it is sorted, we can optimize the search */
|
||||
/* returns TRUE if found, and FALSE not found */
|
||||
/* returns the found key and the index where it was found in parameters */
|
||||
/* If the key is not found, the nearest index from the bottom will be returned,
|
||||
/** Find the index of the key that we are looking for.
|
||||
* Since it is sorted, we can optimize the search.
|
||||
* returns TRUE if found, and FALSE not found.
|
||||
* Returns the found key and the index where it was found in parameters.
|
||||
* If the key is not found, the nearest index from the bottom will be returned,
|
||||
* allowing the ability to find where an key should go into the list.
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
* @param key Key to search for
|
||||
* @param pIndex Pointer to the variable taking the index were the key
|
||||
* had been found.
|
||||
*
|
||||
* @return TRUE if found, and FALSE if not
|
||||
*/
|
||||
/* allowing the ability to find where an key should go into the list. */
|
||||
static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
|
||||
{
|
||||
struct Keylist_Node *node; /* holds the new node */
|
||||
@@ -127,7 +146,12 @@ static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
|
||||
int index = 0; /* our current search place in the array */
|
||||
KEY current_key = 0; /* place holder for current node key */
|
||||
int status = FALSE; /* return value */
|
||||
if (!list || !list->array || !list->count) {
|
||||
|
||||
if (!list) {
|
||||
*pIndex = 0;
|
||||
return (FALSE);
|
||||
}
|
||||
if (!list->array || !list->count) {
|
||||
*pIndex = 0;
|
||||
return (FALSE);
|
||||
}
|
||||
@@ -148,12 +172,12 @@ static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
|
||||
left = index + 1;
|
||||
}
|
||||
} while ((key != current_key) && (left <= right));
|
||||
|
||||
if (key == current_key) {
|
||||
status = TRUE;
|
||||
*pIndex = index;
|
||||
}
|
||||
|
||||
else {
|
||||
} else {
|
||||
/* where the index should be */
|
||||
if (key > current_key) {
|
||||
*pIndex = index + 1;
|
||||
@@ -168,7 +192,15 @@ static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
|
||||
/******************************************************************** */
|
||||
/* list data functions */
|
||||
/******************************************************************** */
|
||||
/* inserts a node into its sorted position */
|
||||
/** Inserts a node into its sorted position.
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
* @param key Key to be inserted
|
||||
* @param data Pointer to the data hold by the key.
|
||||
* This pointer needs to be poiting to static memory
|
||||
* as it will be stored in the list and later used
|
||||
* by retrieving the key again.
|
||||
*/
|
||||
int Keylist_Data_Add(OS_Keylist list, KEY key, void *data)
|
||||
{
|
||||
struct Keylist_Node *node; /* holds the new node */
|
||||
@@ -191,9 +223,7 @@ int Keylist_Data_Add(OS_Keylist list, KEY key, void *data)
|
||||
for (i = list->count; i > index; i--) {
|
||||
list->array[i] = list->array[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
@@ -209,138 +239,199 @@ int Keylist_Data_Add(OS_Keylist list, KEY key, void *data)
|
||||
return index;
|
||||
}
|
||||
|
||||
/* deletes a node specified by its index */
|
||||
/* returns the data from the node */
|
||||
/** Deletes a node specified by its index
|
||||
* returns the data from the node
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
* @param index Index of the key to be deleted
|
||||
*
|
||||
* @returns Pointer to the data of the deleted key or NULL.
|
||||
*/
|
||||
void *Keylist_Data_Delete_By_Index(OS_Keylist list, int index)
|
||||
{
|
||||
struct Keylist_Node *node;
|
||||
void *data = NULL;
|
||||
|
||||
if (list && list->array && list->count && (index >= 0) &&
|
||||
(index < list->count)) {
|
||||
node = list->array[index];
|
||||
if (node)
|
||||
data = node->data;
|
||||
|
||||
/* move the nodes to account for the deleted one */
|
||||
if (list->count == 1) {
|
||||
/* There is no node shifting to do */
|
||||
} else if (index == (list->count - 1)) {
|
||||
/* We are the last one */
|
||||
/* There is no node shifting to do */
|
||||
} else {
|
||||
/* Move all the nodes down one */
|
||||
int i; /* counter */
|
||||
int count = list->count - 1;
|
||||
for (i = index; i < count; i++) {
|
||||
list->array[i] = list->array[i + 1];
|
||||
if (list) {
|
||||
if (list->array && list->count && (index >= 0) &&
|
||||
(index < list->count)) {
|
||||
node = list->array[index];
|
||||
if (node) {
|
||||
data = node->data;
|
||||
}
|
||||
/* move the nodes to account for the deleted one */
|
||||
if (list->count == 1) {
|
||||
/* There is no node shifting to do */
|
||||
} else if (index == (list->count - 1)) {
|
||||
/* We are the last one */
|
||||
/* There is no node shifting to do */
|
||||
} else {
|
||||
/* Move all the nodes down one */
|
||||
int i; /* counter */
|
||||
int count = list->count - 1;
|
||||
for (i = index; i < count; i++) {
|
||||
list->array[i] = list->array[i + 1];
|
||||
}
|
||||
}
|
||||
list->count--;
|
||||
if (node) {
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
list->count--;
|
||||
if (node) {
|
||||
free(node);
|
||||
}
|
||||
|
||||
/* potentially reduce the size of the array */
|
||||
(void)CheckArraySize(list);
|
||||
/* potentially reduce the size of the array */
|
||||
(void)CheckArraySize(list);
|
||||
}
|
||||
}
|
||||
return (data);
|
||||
}
|
||||
|
||||
/* deletes a node specified by its key */
|
||||
/* returns the data from the node */
|
||||
/** Deletes a node specified by its key/
|
||||
* returns the data from the node
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
* @param key Key to be deleted
|
||||
*
|
||||
* @returns Pointer to the data of the deleted key or NULL.
|
||||
*/
|
||||
void *Keylist_Data_Delete(OS_Keylist list, KEY key)
|
||||
{
|
||||
void *data = NULL; /* return value */
|
||||
int index; /* where the node is in the array */
|
||||
|
||||
if (list) {
|
||||
if (FindIndex(list, key, &index))
|
||||
if (FindIndex(list, key, &index)) {
|
||||
data = Keylist_Data_Delete_By_Index(list, index);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* returns the data from last node, and removes it from the list */
|
||||
/** Returns the data from last node, and
|
||||
* removes it from the list.
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
*
|
||||
* @return Pointer to the data that might be NULL.
|
||||
*/
|
||||
void *Keylist_Data_Pop(OS_Keylist list)
|
||||
{
|
||||
void *data = NULL; /* return value */
|
||||
int index; /* position in the array */
|
||||
|
||||
if (list && list->count) {
|
||||
index = list->count - 1;
|
||||
data = Keylist_Data_Delete_By_Index(list, index);
|
||||
if (list) {
|
||||
if (list->count) {
|
||||
index = list->count - 1;
|
||||
data = Keylist_Data_Delete_By_Index(list, index);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* returns the data from the node specified by key */
|
||||
/** Returns the data from the node specified by key.
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
* @param key Key to be deleted
|
||||
*
|
||||
* @returns Pointer to the data, that might be NULL.
|
||||
*/
|
||||
void *Keylist_Data(OS_Keylist list, KEY key)
|
||||
{
|
||||
struct Keylist_Node *node = NULL;
|
||||
int index = 0; /* used to look up the index of node */
|
||||
|
||||
if (list && list->array && list->count) {
|
||||
if (FindIndex(list, key, &index))
|
||||
node = list->array[index];
|
||||
if (list) {
|
||||
if (list->array && list->count) {
|
||||
if (FindIndex(list, key, &index)) {
|
||||
node = list->array[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return node ? node->data : NULL;
|
||||
}
|
||||
|
||||
/* returns the index from the node specified by key */
|
||||
/** Returns the index from the node specified by key.
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
* @param key Key whose index shall be retrieved.
|
||||
*
|
||||
* @return Index of the key or -1, if not found.
|
||||
*/
|
||||
int Keylist_Index(OS_Keylist list, KEY key)
|
||||
{
|
||||
int index = -1; /* used to look up the index of node */
|
||||
|
||||
if (list && list->array && list->count) {
|
||||
if (!FindIndex(list, key, &index)) {
|
||||
index = -1;
|
||||
if (list) {
|
||||
if (list->array && list->count) {
|
||||
if (!FindIndex(list, key, &index)) {
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/* returns the data specified by index */
|
||||
/** Returns the data specified by index
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
* @param index Key whose data shall be retrieved.
|
||||
*
|
||||
* @return Pointer to the data that might be NULL.
|
||||
*/
|
||||
void *Keylist_Data_Index(OS_Keylist list, int index)
|
||||
{
|
||||
struct Keylist_Node *node = NULL;
|
||||
|
||||
if (list && list->array && list->count && (index >= 0) &&
|
||||
(index < list->count))
|
||||
node = list->array[index];
|
||||
|
||||
if (list) {
|
||||
if (list->array && list->count && (index >= 0) &&
|
||||
(index < list->count)) {
|
||||
node = list->array[index];
|
||||
}
|
||||
}
|
||||
return node ? node->data : NULL;
|
||||
}
|
||||
|
||||
/* return the key at the given index */
|
||||
/** Return the key at the given index.
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
* @param index Index that shall be returned
|
||||
*
|
||||
* @return Key for the index or 0.
|
||||
*/
|
||||
KEY Keylist_Key(OS_Keylist list, int index)
|
||||
{
|
||||
KEY key = 0; /* return value */
|
||||
struct Keylist_Node *node;
|
||||
|
||||
if (list && list->array && list->count && (index >= 0) &&
|
||||
(index < list->count)) {
|
||||
node = list->array[index];
|
||||
if (node)
|
||||
key = node->key;
|
||||
if (list) {
|
||||
if (list->array && list->count && (index >= 0) &&
|
||||
(index < list->count)) {
|
||||
node = list->array[index];
|
||||
if (node) {
|
||||
key = node->key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/* returns the next empty key from the list */
|
||||
/** Returns the next empty key from the list.
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
* @param key Key whose index shall be retrieved.
|
||||
*
|
||||
* @return Next empty key or 'key=key' if there is none.
|
||||
*/
|
||||
KEY Keylist_Next_Empty_Key(OS_Keylist list, KEY key)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (list) {
|
||||
while (FindIndex(list, key, &index)) {
|
||||
if (KEY_LAST(key))
|
||||
if (KEY_LAST(key)) {
|
||||
break;
|
||||
}
|
||||
key++;
|
||||
}
|
||||
}
|
||||
@@ -348,29 +439,47 @@ KEY Keylist_Next_Empty_Key(OS_Keylist list, KEY key)
|
||||
return key;
|
||||
}
|
||||
|
||||
/* return the number of nodes in this list */
|
||||
/** Return the number of nodes in this list.
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
*
|
||||
* @return Count of key in the list.
|
||||
*/
|
||||
int Keylist_Count(OS_Keylist list)
|
||||
{
|
||||
return list->count;
|
||||
int cnt = 0;
|
||||
|
||||
if (list) {
|
||||
cnt = list->count;
|
||||
}
|
||||
|
||||
return(cnt);
|
||||
}
|
||||
|
||||
/******************************************************************** */
|
||||
/* Public List functions */
|
||||
/******************************************************************** */
|
||||
|
||||
/* returns head of the list or NULL on failure. */
|
||||
/** Returns head of the list or NULL on failure.
|
||||
*
|
||||
* @return Pointer to the key list or NUL if creation failed.
|
||||
*/
|
||||
OS_Keylist Keylist_Create(void)
|
||||
{
|
||||
struct Keylist *list;
|
||||
|
||||
list = KeylistCreate();
|
||||
if (list)
|
||||
if (list) {
|
||||
CheckArraySize(list);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* delete specified list */
|
||||
/** Delete specified list.
|
||||
*
|
||||
* @param list Pointer to the list
|
||||
*/
|
||||
void Keylist_Delete(OS_Keylist list)
|
||||
{ /* list number to be deleted */
|
||||
if (list) {
|
||||
@@ -378,8 +487,9 @@ void Keylist_Delete(OS_Keylist list)
|
||||
while (list->count) {
|
||||
(void)Keylist_Data_Delete_By_Index(list, 0);
|
||||
}
|
||||
if (list->array)
|
||||
if (list->array) {
|
||||
free(list->array);
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user