ngspice/src/frontend/dimens.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 */