Remodel logic of the size that integer items occupy in terminal write

Integers can be hexadecimal, decimal or octal. An optional case-insensitive
suffix specifies their size: HH: 8 bit, H/S: 16 bit, L: 32 bit, LL: 64 bit

An optional U suffix makes a number unsigned. Ordinary 0x hex numbers are
always treated as unsigned. +0x or -0x hex numbers are treated as signed
unless they have a U suffix. Unsigned integers cannot be larger than 2^64-1.

If n is an unsigned integer then -n is also a valid unsigned integer as in C.

Signed integers must fall into the [-2^63, 2^63-1] range or a correspondingly
smaller range when a suffix specifies a smaller type. Out of range signed
numbers trigger a warning.

Ordinary 0x hex numbers with n hex digits (counting leading zeros) use
the smallest size of 1, 2, 4 and 8 bytes that can accommodate any n-digit hex
number. If a suffix specifies a size explicitly the corresponding number of
least significant bytes are written. Otherwise, signed and unsigned integers
alike occupy the smallest of 1, 2, 4, or 8 bytes needed to accommodate them
in their respective representation.
This commit is contained in:
Stefan Rueger 2022-07-12 11:32:38 +01:00
parent 62d3eebd56
commit 51355d04fb
1 changed files with 91 additions and 66 deletions

View File

@ -419,6 +419,7 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
union { union {
float f; float f;
int64_t ll; int64_t ll;
uint64_t ull;
uint8_t a[8]; uint8_t a[8];
}; };
} data = { } data = {
@ -444,44 +445,99 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
data.str_ptr = NULL; data.str_ptr = NULL;
} }
// Get suffix if present // Try integers and assign data size
char suffix = 0, lsuffix = 0; errno = 0;
if(arglen > 1) { data.ull = strtoull(argi, &end_ptr, 0);
suffix = toupper(argi[arglen - 1]); if (!(end_ptr == argi || errno)) {
lsuffix = toupper(argi[arglen - 2]); unsigned int nu=0, nl=0, nh=0, ns=0, nx=0;
if (suffix == 'L' && lsuffix == 'L') { char *p;
argi[arglen -= 2] = '\0';
data.size = 8; // parse suffixes: ULL, LL, UL, L ... UHH, HH
} else if (suffix == 'L' || suffix == 'l') { for(p=end_ptr; *p; p++)
argi[--arglen] = '\0'; switch(toupper(*p)) {
data.size = 4; case 'U': nu++; break;
} else if ((suffix == 'F') && case 'L': nl++; break;
strncmp(argi, "0x", 2) && strncmp(argi, "-0x", 3) && strncmp(argi, "+0x", 3)) { case 'H': nh++; break;
argi[--arglen] = '\0'; case 'S': ns++; break;
data.size = 4; default: nx++;
} else if (suffix == 'H' && lsuffix == 'H') { }
argi[arglen -= 2] = '\0';
if(nx==0 && nu<2 && nl<3 && nh<3 && ns<2) { // could be valid integer suffix
if(nu==0 || toupper(*end_ptr) == 'U' || toupper(p[-1]) == 'U') { // if U, then must be at start or end
bool is_hex = strncasecmp(argi, "0x", 2) == 0; // ordinary hex: "0x..." without explicit +/- sign
bool is_signed = !(nu || is_hex); // neither explicitly unsigned nor ordinary hex
bool is_outside_int64_t = 0;
bool is_out_of_range = 0;
int nhexdigs = p-argi-2;
if(is_signed) { // Is input in range for int64_t?
errno = 0; (void) strtoll(argi, NULL, 0);
is_outside_int64_t = errno == ERANGE;
}
if(nl==0 && ns==0 && nh==0) { // no explicit data size
// ordinary hex numbers have "implicit" size, given by number of hex digits, including leading zeros
if(is_hex) {
data.size = nhexdigs > 8? 8: nhexdigs > 4? 4: nhexdigs > 2? 2: 1;
} else if(is_signed) {
// smallest size that fits signed representation
data.size =
is_outside_int64_t? 8:
data.ll < INT32_MIN || data.ll > INT32_MAX? 8:
data.ll < INT16_MIN || data.ll > INT16_MAX? 4:
data.ll < INT8_MIN || data.ll > INT8_MAX? 2: 1;
} else {
// smallest size that fits unsigned representation
data.size =
data.ull > UINT32_MAX? 8:
data.ull > UINT16_MAX? 4:
data.ull > UINT8_MAX? 2: 1;
}
} else if(nl==0 && nh==2 && ns==0) { // HH
data.size = 1; data.size = 1;
} else if (suffix == 'H' || suffix == 'S') { if(is_outside_int64_t || (is_signed && (data.ll < INT8_MIN || data.ll > INT8_MAX))) {
argi[--arglen] = '\0'; is_out_of_range = 1;
data.ll = (int8_t) data.ll;
}
} else if(nl==0 && ((nh==1 && ns==0) || (nh==0 && ns==1))) { // H or S
data.size = 2; data.size = 2;
} else if (suffix == '\'') { if(is_outside_int64_t || (is_signed && (data.ll < INT16_MIN || data.ll > INT16_MAX))) {
data.size = 1; is_out_of_range = 1;
data.ll = (int16_t) data.ll;
}
} else if(nl==1 && nh==0 && ns==0) { // L
data.size = 4;
if(is_outside_int64_t || (is_signed && (data.ll < INT32_MIN || data.ll > INT32_MAX))) {
is_out_of_range = 1;
data.ll = (int32_t) data.ll;
}
} else if(nl==2 && nh==0 && ns==0) { // LL
data.size = 8;
}
if(is_outside_int64_t || is_out_of_range)
avrdude_message(MSG_INFO, "%s (write): %s out of int%d_t range, "
"interpreted as %d-byte %lld%s; consider 'U' suffix\n",
progname, argi, data.size*8, data.size, data.ll,
is_out_of_range? " (unlikely what you want)": ""
);
}
} }
} }
// Try integers if(!data.size) { // Data item was not recognised as integer
errno = 0;
data.ll = strtoull(argi, &end_ptr, 0);
if (!(end_ptr == argi || errno)) {
// Try float // Try float
data.f = strtof(argi, &end_ptr); data.f = strtof(argi, &end_ptr);
if (end_ptr != argi && toupper(*end_ptr) == 'F' && end_ptr[1] == 0) {
data.is_float = true; data.is_float = true;
if (*end_ptr || (end_ptr == argi)) { data.size = 4;
data.is_float = false; } else {
// Try single character // Try single character
if (argi[0] == '\'' && argi[2] == '\'') { if (argi[0] == '\'' && argi[2] == '\'') {
data.ll = argi[1]; data.ll = argi[1];
data.size = 1;
} else { } else {
// Try string that starts and ends with quotes // Try string that starts and ends with quotes
if (argi[0] == '\"' && argi[arglen - 1] == '\"') { if (argi[0] == '\"' && argi[arglen - 1] == '\"') {
@ -503,35 +559,6 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
} }
} }
} }
// Print warning if data size might be ambiguous
bool is_hex = (strncmp(argi, "0x", 2) == 0);
bool is_neg_hex = (strncmp(argi, "-0x", 3) == 0);
bool leading_zero = (strncmp(argi, "0x0", 3) == 0);
int8_t hex_digits = (arglen - 2);
if(!data.size // No pre-defined size
&& (is_neg_hex // Hex with - sign in front
|| (is_hex && leading_zero && (hex_digits & (hex_digits - 1))) // Hex with 3, 5, 6 or 7 digits
|| (!is_hex && !data.is_float && llabs(data.ll) > 0xFF && arglen > 2))) // Base10 int greater than 255
{
avrdude_message(MSG_INFO, "Warning: no size suffix specified for \"%s\". "
"Writing %d byte(s)\n",
argi,
llabs(data.ll) > UINT32_MAX ? 8 :
llabs(data.ll) > UINT16_MAX || data.is_float ? 4 : \
llabs(data.ll) > UINT8_MAX ? 2 : 1);
}
// Adjust size if signed integer
if (data.ll < 0 && !data.is_float) {
if (data.ll < INT32_MIN)
data.size = 8;
else if (data.ll < INT16_MIN)
data.size = 4;
else if (data.ll < INT8_MIN)
data.size = 2;
else
data.size = 1;
}
} }
if(data.str_ptr) { if(data.str_ptr) {
for(int16_t j = 0; j < strlen(data.str_ptr); j++) for(int16_t j = 0; j < strlen(data.str_ptr); j++)
@ -1175,5 +1202,3 @@ int terminal_mode(PROGRAMMER * pgm, struct avrpart * p)
return rc; return rc;
} }