Files
microser/mmslib/mmsl/ms_size.c

601 lines
19 KiB
C
Raw Normal View History

2026-06-15 15:48:16 +08:00
/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/* (c) Copyright Systems Integration Specialists Company, Inc., */
/* 1986 - 2001, All Rights Reserved */
/* */
/* PROPRIETARY AND CONFIDENTIAL */
/* */
/* MODULE NAME : ms_size.c */
/* PRODUCT(S) : MMSEASE */
/* */
/* MODULE DESCRIPTION : */
/* This module contains support functions for estimating maximum */
/* size of Variable Access service PDUs (Read, Write, and InfoRpt).*/
/* */
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */
/* */
/* MODIFICATION LOG : */
/* Date Who Rev Comments */
/* -------- --- ------ ------------------------------------------- */
/* 04/14/03 JRB 09 Eliminate compiler warnings. */
/* 04/02/03 JRB 08 Add UTF8string support (see RT_UTF8_STRING). */
/* 11/14/01 EJV 07 Added support for new MMS type UtcTime: */
/* ms_data_size: added case for RT_UTC_TIME */
/* 10/18/00 RKR 06 Changed RT_UNSIGNED in ms_data_size */
/* 09/15/00 MDE 05 Fixed MMS-EASE compile error */
/* 09/07/00 MDE 04 Added MMS-LITE support */
/* 02/10/98 MDE 03 Now don't use runtime type loops element */
/* 08/15/97 MDE 02 Minor cleanup */
/* 06/09/97 MDE 01 Changed Runtime Type, and it's use */
/* 04/02/97 DTL 7.00 MMSEASE 7.0 release. See MODL70.DOC for */
/* history. */
/************************************************************************/
#include "glbtypes.h"
#include "sysincs.h"
#include "glbsem.h"
#include "mmsdefs.h"
#include "mms_pvar.h"
#include "mms_vvar.h"
#include "asn1defs.h"
#if defined(MMS_LITE)
#include "mvl_defs.h"
#endif
/************************************************************************/
/* For debug version, use a static pointer to avoid duplication of */
/* __FILE__ strings. */
#if defined(DEBUG_SISCO) && defined(S_MT_SUPPORT)
static ST_CHAR *thisFileName = __FILE__;
#endif
static ST_LONG address_size (VAR_ACC_ADDR *address, ST_BOOLEAN req);
static ST_LONG obj_name_size (OBJECT_NAME *name, ST_BOOLEAN req);
static ST_LONG ms_data_size (RUNTIME_TYPE *rt_ptr, ST_INT rt_num,
ST_INT alt_len, ST_UCHAR *alt_ptr,
ST_BOOLEAN req);
static ST_INT arr_loop_level;
static ST_INT *arr_loops;
/************************************************************************/
/************************************************************************/
/* To allow the user to specify either MMS-EASE or UNKNOWN PDU style, */
/* the following definitions & data structures are used. */
/* See ms_size.doc for information on how these were derived. */
ST_INT ms_req_bld_id = UNKNOWN_BUILDER;
ST_INT ms_resp_bld_id = UNKNOWN_BUILDER;
/* Because the MMS-EASE ASN.1 tools are a bit conservitive, need to */
/* provide for a little extra overhead. */
ST_INT ms_size_oh_pad = ASN1_MAX_ELEMENT_OVERHEAD;
ST_LONG large_el_oh[] =
{
6L, /* MMS-EASE is PDU Builder */
8L /* UNKNOWN PDU Builder */
};
ST_LONG med_el_oh[] =
{
4L, /* MMS-EASE is PDU Builder */
8L /* UNKNOWN PDU Builder */
};
ST_LONG small_el_oh[] =
{
2L, /* MMS-EASE is PDU Builder */
8L /* UNKNOWN PDU Builder */
};
#define LARGE_EL_REQ_OH large_el_oh[ms_req_bld_id]
#define MED_EL_REQ_OH med_el_oh[ms_req_bld_id]
#define SMALL_EL_REQ_OH small_el_oh[ms_req_bld_id]
#define LARGE_EL_RESP_OH large_el_oh[ms_resp_bld_id]
#define MED_EL_RESP_OH med_el_oh[ms_resp_bld_id]
#define SMALL_EL_RESP_OH small_el_oh[ms_resp_bld_id]
/************************************************************************/
/************************************************************************/
/* Overhead for various types of MMS ASN.1 data elements */
#define CONF_REQ_PDU_OH (ms_size_oh_pad+MED_EL_REQ_OH+SMALL_EL_REQ_OH+4)
#define CONF_RESP_PDU_OH (ms_size_oh_pad+MED_EL_RESP_OH+SMALL_EL_RESP_OH+4)
#define UNCONF_PDU_OH (ms_size_oh_pad+MED_EL_REQ_OH)
/* Overhead for empty PDU's of various types */
#define READ_REQ_OH (CONF_REQ_PDU_OH + (MED_EL_REQ_OH * 2))
#define READ_RESP_OH (CONF_RESP_PDU_OH + (MED_EL_RESP_OH * 2))
#define WRITE_REQ_OH (CONF_REQ_PDU_OH + (MED_EL_REQ_OH * 2))
#define WRITE_RESP_OH (CONF_RESP_PDU_OH + MED_EL_RESP_OH)
#define INFO_REQ_OH (UNCONF_PDU_OH + (MED_EL_REQ_OH * 2))
/* Overhead for various components of interest */
#define SPEC_IN_RSLT_SIZE (MED_EL_REQ_OH + 1)
/* Object name overhead */
#define VMD_OBJ_NAME_REQ_OH SMALL_EL_REQ_OH
#define DOM_OBJ_NAME_REQ_OH (MED_EL_REQ_OH + (SMALL_EL_REQ_OH * 2))
#define AA_OBJ_NAME_REQ_OH SMALL_EL_REQ_OH
#define VMD_OBJ_NAME_RESP_OH SMALL_EL_RESP_OH
#define DOM_OBJ_NAME_RESP_OH (MED_EL_RESP_OH + (SMALL_EL_RESP_OH * 2))
#define AA_OBJ_NAME_RESP_OH SMALL_EL_RESP_OH
#define GENTIME_LEN 18L
/************************************************************************/
/************************************************************************/
/************************************************************************/
/* ms_init_va_size */
/* This function is called from the user as a first step in estimating */
/* the maximum size of both the request and response PDUs of any of the */
/* Variable Access services (Read, Write, and InformationReport). This */
/* function initializes the values pointed to by req_size and resp_size */
/* to the overhead in the PDU (before determining the size of the data */
/* and specification of each variable to be included). The companion */
/* function ms_va_size is then called for each variable to add on the */
/* estimated size attributable to that variable. This function returns */
/* 0 for success, else an error code. */
/************************************************************************/
ST_RET ms_init_va_size (ST_INT op, ST_UCHAR spec_in_rslt, ST_INT16 var_acc_tag,
OBJECT_NAME *vl_name, ST_LONG *req_size, ST_LONG *resp_size)
{
ST_RET ret;
S_LOCK_COMMON_RESOURCES ();
ret = SD_SUCCESS;
switch (op)
{
case MMSOP_READ:
/* initialize sizes to include all overhead for basic request */
*req_size = READ_REQ_OH;
*resp_size = READ_RESP_OH;
/* To send request for spec in result takes extra bytes, do does */
/* sending the VA spec in the response. */
if (spec_in_rslt)
{
*req_size += SPEC_IN_RSLT_SIZE;
*resp_size += MED_EL_RESP_OH;
}
break;
case MMSOP_WRITE:
/* initialize sizes to include all overhead for basic request */
*req_size = WRITE_REQ_OH;
*resp_size = WRITE_RESP_OH;
break;
case MMSOP_INFO_RPT:
/* initialize sizes to include all overhead for basic request */
*req_size = INFO_REQ_OH;
*resp_size = 0;
break;
default :
ret = MVE_WRONG_OP;
}
/* If the variable specification is a named list, add in the name, else */
/* add in the sequence of sequence constructor overhead */
/* Note that this is done here, NOT in later calls to ms_va_size */
if (var_acc_tag == VAR_ACC_NAMEDLIST) /* list of variables case */
*req_size += MED_EL_REQ_OH + obj_name_size (vl_name,SD_TRUE);
else
*req_size += MED_EL_REQ_OH; /* seq of seq */
S_UNLOCK_COMMON_RESOURCES ();
return (ret);
}
/************************************************************************/
/* ms_va_size */
/* This function is called from the user repetitively for each variable */
/* expected to be included in a Read, Write, or InfoRpt PDU. Note that */
/* the function ms_init_va_size should be called first to initialize */
/* the two outputs req_size and resp_size. The values pointed to by */
/* req_size and resp_size are incremented by the appropriate amount, */
/* so that after this function has been called for all variables, they */
/* represent valid estimates of the size of the request and response */
/* PDUs for the given service (indicated by op). In some cases, the */
/* type of the variable may be indicated both by the vl_ptr argument */
/* and by the type argument; in this case, both must still be specified */
/* and it is up to the user to verify that they are equal. This */
/* function returns 0 for success, else an error code. */
/* */
/* NOTE: In the case of accessing a variable list object (as opposed */
/* to a list of variable objects), a variable_list structure must */
/* be built and filled in appropriately, with var_spec_tag set to */
/* 5. (Otherwise, the value pointed to by req_size and, in */
/* certain cases, the value pointed to by resp_size will be */
/* much larger than necessary. */
/************************************************************************/
#if !defined(MMS_LITE)
ST_RET ms_va_size (ST_INT op, ST_UCHAR spec_in_rslt, ST_INT16 var_acc_tag,
VARIABLE_LIST *vl_ptr, NAMED_TYPE *type,
ST_LONG *req_size, ST_LONG *resp_size)
#else
ST_RET ms_va_size (ST_INT op, ST_UCHAR spec_in_rslt, ST_INT16 var_acc_tag,
VARIABLE_LIST *vl_ptr, ST_INT type_id,
ST_LONG *req_size, ST_LONG *resp_size)
#endif
{
ST_LONG size;
ST_INT alt_acc_len;
RUNTIME_TYPE *rt;
ST_INT rt_num;
#if defined(MMS_LITE)
ST_INT rc;
#endif
#if !defined(MMS_LITE)
rt = type->rt_head;
rt_num = type->rt_num;
#else
rc = mvl_get_runtime (type_id, &rt, &rt_num);
if (rc != SD_SUCCESS)
return (rc);
#endif
S_LOCK_COMMON_RESOURCES ();
/* non functional at this time */
if (vl_ptr->alt_access_pres)
alt_acc_len = vl_ptr->alt_access.len;
else
alt_acc_len = 0;
/* get the size of the data element based on the MMS type and add the */
/* data element size to the target PDU */
if (op == MMSOP_READ) /* read response carries data */
{
size = ms_data_size (rt, rt_num, alt_acc_len,
vl_ptr->alt_access.data,SD_FALSE);
*resp_size += size;
}
else /* WRITE or INFO */
{
size = ms_data_size (rt, rt_num, alt_acc_len,
vl_ptr->alt_access.data,SD_TRUE);
*req_size += size;
if (op == MMSOP_WRITE) /* Write response carries result */
*resp_size += SMALL_EL_RESP_OH + 1; /* assume failure (+1) */
}
if (size <= 0) /* verify the data size was OK */
{
S_UNLOCK_COMMON_RESOURCES ();
return (MVE_TYPENAME);
}
/* Now calculate the size of the variable access spec for this var. */
/* Only req'd if var_acc_tag is list of vars (list name added already) */
if (var_acc_tag == VAR_ACC_VARLIST)
{
size = MED_EL_REQ_OH;
switch (vl_ptr->var_spec.var_spec_tag)
{
case VA_SPEC_NAMED :
size += obj_name_size (&vl_ptr->var_spec.vs.name,SD_TRUE);
break;
#if !defined(MMS_LITE)
case VA_SPEC_DESCRIBED : /* described gets address & type */
size += type->asn1len +
address_size (&vl_ptr->var_spec.vs.address,SD_TRUE);
break;
#endif
case VA_SPEC_ADDRESSED :
size += address_size (&vl_ptr->var_spec.vs.address,SD_TRUE);
break;
case VA_SPEC_SCATTERED : /* not currently supported */
case VA_SPEC_INVALIDATED :
default:
S_UNLOCK_COMMON_RESOURCES ();
return (MVE_VM_SERVICE_NOTSUPP);
}
/* not really supported at this time, leave in anyway */
if (vl_ptr->alt_access_pres)
size += (ST_LONG) alt_acc_len;
/* Now we have the variable specification size, add to the request */
*req_size += size;
/* Now, if spec in result requested, calculate VA spec size for resp */
if (op == MMSOP_READ && spec_in_rslt)
{
size = MED_EL_RESP_OH;
switch (vl_ptr->var_spec.var_spec_tag)
{
case VA_SPEC_NAMED :
size += obj_name_size (&vl_ptr->var_spec.vs.name,SD_FALSE);
break;
#if !defined(MMS_LITE)
case VA_SPEC_DESCRIBED : /* described gets address & type */
size += type->asn1len +
address_size (&vl_ptr->var_spec.vs.address,SD_FALSE);
break;
#endif
case VA_SPEC_ADDRESSED :
size += address_size (&vl_ptr->var_spec.vs.address,SD_FALSE);
break;
case 5: /* variable list object case */
S_UNLOCK_COMMON_RESOURCES ();
return (SD_SUCCESS); /* No variable spec. req'd */
break;
case VA_SPEC_SCATTERED : /* not currently supported */
case VA_SPEC_INVALIDATED :
default:
S_UNLOCK_COMMON_RESOURCES ();
return (MVE_VM_SERVICE_NOTSUPP);
}
*resp_size += size;
}
} /* if VAR_ACC_VARLIST */
/* Now, if WRITE, need to add write result */
S_UNLOCK_COMMON_RESOURCES ();
return (SD_SUCCESS);
}
/************************************************************************/
/* obj_name_size */
/* This function is called from ms_init_va_size and ms_va_size to */
/* calculate (exactly) the size that a particular object name will take */
/* up in a request or response PDU for the Read, Write, and InfoReport */
/* services. Returns the size of the object name, or 0 if there is a */
/* problem. */
/************************************************************************/
static ST_LONG obj_name_size (OBJECT_NAME *name, ST_BOOLEAN req)
{
ST_LONG oh;
switch (name->object_tag)
{
case VMD_SPEC :
if (req) /* if calculating for a request */
oh = VMD_OBJ_NAME_REQ_OH;
else
oh = VMD_OBJ_NAME_RESP_OH;
return (oh + (ST_LONG) strlen (name->obj_name.vmd_spec));
break;
case DOM_SPEC :
if (req) /* if calculating for a request */
oh = DOM_OBJ_NAME_REQ_OH;
else
oh = DOM_OBJ_NAME_RESP_OH;
return (oh + (ST_LONG) (strlen (name->obj_name.item_id) +
strlen (name->domain_id)));
break;
case AA_SPEC :
if (req) /* if calculating for a request */
oh = AA_OBJ_NAME_REQ_OH;
else
oh = AA_OBJ_NAME_RESP_OH;
return (oh + (ST_LONG) strlen (name->obj_name.aa_spec));
break;
}
mms_op_err = MVE_OBJECT_SCOPE;
return (0L); /* indicates error condition */
}
/************************************************************************/
/* address_size */
/* This function is called from ms_va_size to calculate (exactly) the */
/* size that a particular address will take up in a request or response */
/* PDU for the Read, Write, and InfoReport services. Returns the size */
/* of the address, or 0 if there is a problem. */
/************************************************************************/
static ST_LONG address_size (VAR_ACC_ADDR *address, ST_BOOLEAN req)
{
ST_LONG size;
ST_LONG numaddr;
switch (address->addr_tag)
{
case NUM_ADDR :
if (req) /* if calculating for a request */
size = SMALL_EL_REQ_OH;
else
size = SMALL_EL_RESP_OH;
numaddr = address->addr.num_addr;
if (numaddr < 128L)
size += 1;
else if (numaddr < 32768L)
size += 2;
else if (numaddr < 8388608L)
size += 3;
else
size += 4;
break;
case SYM_ADDR :
if (req) /* if calculating for a request */
size = MED_EL_REQ_OH;
else
size = MED_EL_RESP_OH;
size += strlen (address->addr.sym_addr);
break;
case UNCON_ADDR :
if (req) /* if calculating for a request */
size = MED_EL_REQ_OH;
else
size = MED_EL_RESP_OH;
size += address->addr.unc_addr.unc_len;
break;
default:
mms_op_err = MVE_ADDR;
return (0);
}
return (size);
}
/************************************************************************/
/************************************************************************/
/************************************************************************/
/* ms_data_size */
/* This function is called from the user or the MMS-EASE virtual */
/* machine to estimate the size that the data corresponding to a */
/* particular type will take up in a request or response PDU for the */
/* Read, Write, and InfoReport services. Returns the size of the data, */
/* or 0 if there is a problem. (A rough error code can be found in */
/* mms_op_err; a more detailed one in asn1_pdu_dec_err.) */
/* */
/* Note: Alternate access may be used to modify a type, thereby */
/* affecting the size of the corresponding data. However, */
/* the alternate access inputs are disregarded at this time. */
/* This will be fixed in a later revision. */
/************************************************************************/
static ST_LONG ms_data_size (RUNTIME_TYPE *rt_ptr, ST_INT rt_num,
ST_INT alt_len, ST_UCHAR *alt_ptr,
ST_BOOLEAN req)
{
ST_LONG size;
RUNTIME_TYPE *rt_end;
ST_LONG med_oh;
ST_LONG small_oh;
ST_UINT ellen;
ST_LONG len;
ST_INT arr_loop_buf[ASN1_MAX_LEVEL];
ST_UINT el_size; /* num bytes to encode data */
arr_loop_level = 0;
arr_loops = arr_loop_buf;
if (req) /* if calculating for a request */
{
med_oh = MED_EL_REQ_OH;
small_oh = SMALL_EL_REQ_OH;
}
else /* calculating for response */
{
med_oh = MED_EL_RESP_OH;
small_oh = SMALL_EL_RESP_OH;
}
rt_end = rt_ptr + rt_num; /* end block */
size = 0;
while (rt_ptr < rt_end)
{
if (rt_ptr->el_tag == RT_ARR_END) /* treat case of array ending */
{ /* loops set at start of array */
if (--arr_loops[arr_loop_level] > 0) /* if need to do next ar elmnt */
rt_ptr -= rt_ptr->u.arr.num_rt_blks; /* mv rt_ptr to start of arr */
else
--arr_loop_level;
}
ellen = abs (rt_ptr->u.p.el_len); /* make sure positive */
switch (rt_ptr->el_tag)
{
case RT_ARR_START : /* arrays don't have to be aligned */
++arr_loop_level;
arr_loops[arr_loop_level] = rt_ptr->u.arr.num_elmnts;
/* let fall through to STR_MMSOP_START */
case RT_STR_START : /* structure starting */
size += med_oh; /* add in data element overhead */
break;
case RT_UNSIGNED : /* unsigned integer */
size += small_oh + ellen + 1; /* high order bit set needs 0x00*/
break;
case RT_UTC_TIME : /* UTC time */
case RT_BINARY_TIME : /* binary time */
case RT_INTEGER : /* integer */
case RT_FLOATING_POINT : /* float */
size += small_oh + ellen;
break;
case RT_VISIBLE_STRING : /* visible string */
case RT_OCTET_STRING : /* octet string */
size += ellen;
if (ellen < 128)
size += small_oh;
else
size += med_oh;
break;
case RT_GENERAL_TIME : /* generalized time */
size += small_oh + GENTIME_LEN;
break;
case RT_BOOL : /* boolean */
size += small_oh +1;
break;
case RT_BCD : /* bcd */
size += small_oh + ellen;
break;
case RT_BIT_STRING : /* bit string */
len = 1 + CALC_BIT_LEN (ellen);
if (len < 128)
size += small_oh + len; /* add in data element overhead */
else
size += med_oh + len; /* add in data element overhead */
break;
case RT_UTF8_STRING : /* Unicode UTF8 string */
el_size = ellen*4; /* Could take 4 bytes per char */
size += el_size;
if (el_size < 128)
size += small_oh;
else
size += med_oh;
break;
case RT_ARR_END : /* array done */
case RT_STR_END : /* structure done */
break;
default : /* should not be any other tag */
return (0);
break;
}
rt_ptr++; /* point to next block */
} /* while (rt blocks) */
return (size);
}