402 lines
11 KiB
C
402 lines
11 KiB
C
/**********
|
|
Copyright 1992 Regents of the University of California. All rights reserved.
|
|
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
|
**********/
|
|
|
|
/*
|
|
* Read and write dimension/index arrays via strings.
|
|
*/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
#include "ngspice/dvec.h" /* For MAXDIMS */
|
|
#include "dimens.h"
|
|
#include "ngspice/stringskip.h"
|
|
|
|
|
|
static int atodims_bracketed(const char *p, int *data, int *p_n_dim);
|
|
static int atodims_unbracketed(const char *p, int *data, int *p_n_dim);
|
|
static int get_bracketed_dim(const char *p, int *p_val);
|
|
static int atodims_csv(const char *p, int *data, int *p_n_dim);
|
|
static int get_dim(const char *p, int *p_val);
|
|
|
|
|
|
|
|
/*
|
|
* Create a string of the form "12,1,10".
|
|
*
|
|
* Parameters
|
|
* dim_data: Array of sizes of dimensions, [12, 1, 10] for the example
|
|
* n_dim: Number of elements in the array, 3 in the example
|
|
* retstring: Address of buffer where the string is returned.
|
|
|
|
* Remarks
|
|
* It is assumed that the buffer in retstring is large enough, which for
|
|
* MAXDIMS, would require MAXDIMS * 11 bytes in the worst case assuming
|
|
* 32-bit ints. A looser but more general bound only assuming 8-bit bytes
|
|
* would be MAXDIMS * (3 * sizeof(int) + 1).
|
|
*/
|
|
void
|
|
dimstring(const int *dim_data, int n_dim, char *retstring)
|
|
{
|
|
/* Handle case of no dimensions */
|
|
if (dim_data == (int *) NULL || n_dim < 1) {
|
|
*retstring = '\0';
|
|
return;
|
|
}
|
|
|
|
/* Append each dimension */
|
|
retstring += sprintf(retstring, "%d", dim_data[0]); /* first */
|
|
int i;
|
|
for (i = 1; i < n_dim; i++) { /* rest are prefixed by a comma */
|
|
retstring += sprintf(retstring, ",%d", dim_data[i]);
|
|
}
|
|
} /* end of function dimstring */
|
|
|
|
|
|
|
|
/*
|
|
* Create a string of the form "[12][1][10]" in retstring.
|
|
*
|
|
* Parameters
|
|
* dim_data: Array of sizes of dimensions, [12, 1, 10] for the example
|
|
* n_dim: Number of elements in the array, 3 in the example
|
|
* retstring: Address of buffer where the string is returned.
|
|
|
|
* Remarks
|
|
* It is assumed that the buffer in retstring is large enough, which for
|
|
* MAXDIMS, would require MAXDIMS * 12 + 1 bytes in the worst case assuming
|
|
* 32-bit ints. A looser but more general bound only assuming 8-bit bytes
|
|
* would be MAXDIMS * (3 * sizeof(int) + 2) + 1.
|
|
*/
|
|
void
|
|
indexstring(const int *dim_data, int n_dim, char *retstring)
|
|
{
|
|
/* Handle case of no dimensions */
|
|
if (dim_data == (int *) NULL || n_dim < 1) {
|
|
*retstring = '\0';
|
|
return;
|
|
}
|
|
|
|
/* Append each dimension */
|
|
int i;
|
|
for (i = 0; i < n_dim; i++) {
|
|
retstring += sprintf(retstring, "[%d]", dim_data[i]);
|
|
}
|
|
} /* end of function indexstring */
|
|
|
|
|
|
|
|
/*
|
|
* Add one to anstrchr into an array with sizes in dims.
|
|
* Return 1 when all counters overflow at once.
|
|
*/
|
|
int
|
|
incindex(int *counts, int numcounts, const int *dims, int numdims)
|
|
{
|
|
int i, start;
|
|
|
|
if (!counts || numcounts < 1 || !dims || numdims < 1)
|
|
return 0;
|
|
|
|
start = numcounts - 1;
|
|
|
|
for (i = start; i >= 0; i--)
|
|
if (++counts[i] < dims[i])
|
|
break; /* This counter is not maxed out. */
|
|
else
|
|
counts[i] = 0;
|
|
|
|
if (i == 0)
|
|
return (1);
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Read a string of one of the following forms into a dimensions array:
|
|
* [12][1][10]
|
|
* [12,1,10]
|
|
* 12,1,10
|
|
* 12, 1, 10
|
|
* 12 , 1 , 10
|
|
* Basically, we require that all brackets be matched, that all numbers
|
|
* be separated by commas or by "][", that all whitespace is ignored, and
|
|
* the beginning [ and end ] are ignored if they exist. The only valid
|
|
* characters in the string are brackets, commas, spaces, and digits.
|
|
* If any dimension is blank, its entry in the array is set to 0.
|
|
*
|
|
* Return 0 on success, 1 on failure.
|
|
*/
|
|
int atodims(const char *p, int *data, int *p_n_dim)
|
|
{
|
|
/* Validate arguments partially */
|
|
if (!data || !p_n_dim) {
|
|
return 1;
|
|
}
|
|
|
|
/* NULL string = no dimensions */
|
|
if (!p) {
|
|
*p_n_dim = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* Move to first "real" character */
|
|
p = skip_ws(p);
|
|
|
|
/* Allowed first char is [ to start bracked string or a number */
|
|
return *p == '[' ? atodims_bracketed(p, data, p_n_dim) :
|
|
atodims_unbracketed(p, data, p_n_dim);
|
|
} /* end of function atodims */
|
|
|
|
|
|
|
|
/* This function processes a dimension string of the form
|
|
* [1,2,3,4] or [1][2][3][4]. Whitespace is allowed anywhere except
|
|
* at the beginning of the string or between the digits of a dimension.
|
|
*
|
|
* Return codes
|
|
* 0: OK
|
|
* +1: Error
|
|
*/
|
|
static int atodims_bracketed(const char *p, int *data, int *p_n_dim)
|
|
{
|
|
/* Process the first element, which is special because it determines
|
|
* if the string is of form [] or [1,2...] or [1][2]... */
|
|
p = skip_ws(++p); /* Step to number */
|
|
|
|
{
|
|
int rc;
|
|
|
|
/* Get the dimension value exiting with an error on failure */
|
|
if ((rc = get_dim(p, data)) <= 0) { /* no number or overflow */
|
|
if (rc < 0) { /* overflow */
|
|
return +1;
|
|
}
|
|
/* Handle special case of [] */
|
|
if (*p == ']') {
|
|
*p_n_dim = 0;
|
|
return 0;
|
|
}
|
|
return +1; /* else an error */
|
|
}
|
|
|
|
|
|
p = skip_ws(p + rc); /* at comma or ] (or error) */
|
|
switch (*p) {
|
|
case ',': /* form [1,2,... */
|
|
*p_n_dim = 1;
|
|
rc = atodims_csv(++p, data, p_n_dim);
|
|
if (rc <= 1) { /* error or invalid termination */
|
|
return +1; /* Return error */
|
|
}
|
|
|
|
/* Else scan ended with ']', but did it end the string,
|
|
* whitespace excluded? */
|
|
p = skip_ws(p + rc);
|
|
|
|
return *p != '\0';
|
|
case ']': /* form [1][2]... */
|
|
++p; /* step past ']' */
|
|
break;
|
|
default: /* invalid char */
|
|
return +1;
|
|
}
|
|
}
|
|
|
|
/* Continue parsing form [1][2]... */
|
|
unsigned int n_dim = 1; /* already 1 dim from above */
|
|
for ( ; ; ) {
|
|
if (n_dim == MAXDIMS) { /* too many dimensions */
|
|
return +1;
|
|
}
|
|
|
|
int rc = get_bracketed_dim(p, data + n_dim);
|
|
if (rc <= 0) { /* error or normal exit */
|
|
*p_n_dim = (int) n_dim;
|
|
return !!rc;
|
|
}
|
|
p += rc; /* step after the dimension that was processed */
|
|
++n_dim; /* one more dimension */
|
|
} /* end of loop getting dimensions */
|
|
} /* end of function atodims_bracketed */
|
|
|
|
|
|
|
|
/* This function processes a dimension string of the form
|
|
* 1,2,3,4. Whiltespace is allowed anywhere except
|
|
* at the beginning of the string or between the digits of a dimension.
|
|
*
|
|
* Return codes
|
|
* 0: OK
|
|
* +1: Error
|
|
*/
|
|
static int atodims_unbracketed(const char *p, int *data, int *p_n_dim)
|
|
{
|
|
*p_n_dim = 0; /* either "" so 0 or init for atodims_csv */
|
|
|
|
if (*p == '\0') { /* special case of "" */
|
|
return 0;
|
|
}
|
|
|
|
/* Scan comma-separated dimensions. Must end with '\0' (rc=0) */
|
|
return !!atodims_csv(p, data, p_n_dim);
|
|
} /* end of function atodims_unbracked */
|
|
|
|
|
|
|
|
/* This function processes dimension strings of the form
|
|
* 1,2,3,4 and 1,2,3,4]. Whiltespace is allowed anywhere except
|
|
* at the beginning of the string or between the digits of a dimension.
|
|
* On entry, *p_n_dim is the number of dimensions already added to data
|
|
* and p points to the first number to be processed.
|
|
|
|
*
|
|
* Return codes
|
|
* -1: Error
|
|
* 0: OK, scan ended by '\0'
|
|
* >0: OK, scan ended by ']', returned value = # chars processed
|
|
*/
|
|
static int atodims_csv(const char *p, int *data, int *p_n_dim)
|
|
{
|
|
const char *p0 = p;
|
|
unsigned int n_dim = (unsigned int) *p_n_dim;
|
|
for ( ; ; ) {
|
|
int val;
|
|
p = skip_ws(p);
|
|
int rc = get_dim(p, &val);
|
|
if (rc <= 0) { /* No number or overflow */
|
|
return -1;
|
|
}
|
|
|
|
/* Dimension was read */
|
|
if (n_dim >= MAXDIMS) { /* too many dimensions */
|
|
return -1;
|
|
}
|
|
data[n_dim++] = val; /* Add data for this dimension */
|
|
p = skip_ws(p + rc); /* step after the dimension that was processed */
|
|
|
|
/* Should normally be at comma, but there are special cases for
|
|
* end of regular list or bracketed list */
|
|
switch (*p) {
|
|
case ',': /* inter-dimension comma */
|
|
++p;
|
|
break;
|
|
case ']': /* ] ended scan */
|
|
*p_n_dim = (int) n_dim;
|
|
return (int) (p - p0) + 1;
|
|
case '\0': /* end of string ended scan */
|
|
*p_n_dim = (int) n_dim;
|
|
return 0;
|
|
default: /* invalid char */
|
|
return -1;
|
|
} /* end of switch over character ending scan */
|
|
} /* end of loop getting dimensions */
|
|
} /* end of function atodims_csv */
|
|
|
|
|
|
|
|
/* This function gets the dimension value in a string of the form
|
|
* [1] where spaces may appear anywhere except between the digits of the
|
|
* number.
|
|
*
|
|
* Return codes
|
|
* -1: Error
|
|
* 0: String ended before '['
|
|
* >0: Number of characters processed
|
|
*/
|
|
static int get_bracketed_dim(const char *p, int *p_val)
|
|
{
|
|
const char *p0 = p; /* save start */
|
|
p = skip_ws(p); /* move to opening bracket */
|
|
|
|
const char char_cur = *p;
|
|
if (char_cur == '\0') { /* end of string */
|
|
return 0;
|
|
}
|
|
if (char_cur != '[') { /* no bracket */
|
|
return -1;
|
|
}
|
|
p = skip_ws(++p); /* move to dimension */
|
|
|
|
int rc = get_dim(p, p_val); /* read the dimension */
|
|
if (rc <= 0) { /* error */
|
|
return -1;
|
|
}
|
|
|
|
p = skip_ws(p + rc); /* move to closing backet */
|
|
if (*p != ']') { /* no bracket */
|
|
return -1;
|
|
}
|
|
|
|
return (int) (p - p0) + 1;
|
|
} /* end of function get_bracketed_dim */
|
|
|
|
|
|
|
|
/* This function reads the unsigned number at p as a dimension
|
|
*
|
|
* Return codes
|
|
* -1: overflow
|
|
* 0: *p is not a digit
|
|
* >0: Number of characters processed
|
|
*/
|
|
static int get_dim(const char *p, int *p_val)
|
|
{
|
|
unsigned int val = 0;
|
|
const char *p0 = p;
|
|
for ( ; ; ++p) {
|
|
const char c_cur = *p;
|
|
unsigned int digit_cur = (unsigned int) (c_cur - '0');
|
|
unsigned int val_new;
|
|
if (digit_cur > 9) { /* not a digit */
|
|
if ((*p_val = (int) val) < 0) { /* overflow */
|
|
return -1;
|
|
}
|
|
return (int) (p - p0);
|
|
} /* end of case of not a digit */
|
|
if ((val_new = 10 * val + digit_cur) < val) { /* overflow */
|
|
return -1;
|
|
}
|
|
val = val_new; /* update number */
|
|
} /* end of loop over digits */
|
|
} /* end of function get_dim */
|
|
|
|
|
|
|
|
#ifdef COMPILE_UNUSED_FUNCTIONS
|
|
/* #ifdef COMPILE_UNUSED_FUNCTIONS added 2019-03-31 */
|
|
/*
|
|
* Count number of empty dimensions in an array.
|
|
*/
|
|
int
|
|
emptydims(int *data, int length)
|
|
{
|
|
int i, numempty = 0;
|
|
|
|
for (i = 0; i < length; i++)
|
|
if (data[i] == 0)
|
|
numempty++;
|
|
|
|
return (numempty);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Skip to the first character that cannot be part of a dimension string.
|
|
*/
|
|
char *
|
|
skipdims(char *p)
|
|
{
|
|
if (!p)
|
|
return NULL;
|
|
|
|
while (*p && (*p == '[' || *p == ']' || *p == ',' ||
|
|
isspace_c(*p) || isdigit_c(*p)))
|
|
p++;
|
|
|
|
return (p);
|
|
}
|
|
#endif /* COMPILE_UNUSED_FUNCTIONS */
|