-
-
Notifications
You must be signed in to change notification settings - Fork 349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cast to unsigned type when interpreting HID descriptor length bytes (libusb 0.1) #1320
Merged
jimklimov
merged 1 commit into
networkupstools:master
from
nbriggs:1261_libusb0_descriptor_length
Mar 2, 2022
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just wondering in hind-sight: is a
char << 8
safe on all platforms (to produce the high bits of a two-byte word here, I suppose) or would it result in a zero (all bits rotated from sight) or a same value (rotated around, ending where we started), or should there be more casting or similar magic here, to be portable?Like
((uint16_t)buf[7] & 0x00FF) | (((uint16_t)buf[8] & 0x00FF) << 8)
? Or plain multiplication like256 * (uint8_t)buf[8] + (uint8_t)buf[7]
(where256
implicitly dictates anint
for the interim manipulated value, I believe)?Or do the bit maths imply
int
anyway? (Might be so, according to many warnings I "fought" in the past year)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The C integer type-promotion rules are not exactly obvious, but I think that this StackOverflow article is an easier read than the actual spec, and quotes the spec.
To start, left shift,
<<
is never a circular shift.char << 8
(not unsigned char) is not going to produce the expected answer on any system that treats characters as signed. Forchar sc = 0x9a;
,sc << 8
is 0xffff9a00.Both
(uint16_t)
and(uint8_t)
will be promoted to a larger unsigned type before the arithmetic is done -- but note that(uint16_t) (char) 0x9a
is 0xff9a -- so you would be introducing trouble and then trying to compensate for it with the& 0x00ff
instead of just saying what you mean with(uint8_t)
.The type of an integer constant (with no suffix, e.g. 256) is the first type into which the value will fit, see the list in https://en.cppreference.com/w/c/language/integer_constant -- so 256 is an int (not an unsigned int), but that won't matter in the case of
256 * (uint8_t)...
(but you'd get burned by256 * (uint16_t)(char)
, see previous paragraph). I believe it is going to do that arithmetic in an int sized container, so you'd have to think carefully if you were shifting left by 3 bytes with multiplication instead of 1 byte.The net result is that I believe that the suggested change was both portable and quite clear about the programmers intent, without introducing new conversions which would require the reader to go back to the type promotion document.
Comments welcome...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, to actually answer your final question:
Yes, they do -- see this copy of the C99 standard at the section "6.5.7 Bitwise shift operators" --
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed reply, guess I was bitten a long time ago, maybe before C99 behavior was standardized across the board.
I followed up on this idea in #1325 so simplified it back (push pending); would extend the
make check
instead to make sure NUT builds, with whatever tools and CPUs build-systems use, do behave as we expect bit-wise.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went back and looked at the original K&R C book -- and it said
It goes on to say, of the results:
The usual arithmetic conversions are defined this way (section 6.6):
About characters and integers it says (section 6.1):
If one were to read the section on type specifiers (section 8.2) it's not even clear that
unsigned char
was a legal declaration. They finish up with Portability considerations (section 16)When I was first writing code in C, for portability, we had to check the specific compiler & hardware combination to see how to define our own types in terms of the compiler/hardware supported types to get the behavior that we wanted -- at least now the standards give us
<stdint.h>
withint8_t
anduint8_t
types and it's the compiler writer's job to make them work correctly!