Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-111140: Adds PyLong_AsNativeBytes and PyLong_FromNative[Unsigned]Bytes functions#114886

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
zooba merged 22 commits intopython:mainfromzooba:gh-111140
Feb 12, 2024

Conversation

zooba
Copy link
Member

@zoobazooba commentedFeb 2, 2024
edited by bedevere-appbot
Loading

@zooba
Copy link
MemberAuthor

Sharing work so far for feedback/concerns.

I like the unsigned/signed semantics (basically, using "unsigned" suppresses an error where you need one more bit to be a sign bit), and I dislike the mess needed to support byteswapping for non-native endian on arbitrary length buffers. But it's written now and seems to work, so I guess it can stay.

The "FromByteArray" prototypes are a lie right now, but they're coming in next :) Probably need a "WithOptions" variant as well, though I have a feeling the implementation can't be any better than_PyLong_FromByteArray so it should be simple.

@zoobazooba changed the titlegh-111140: Adds PyLong_AsByteArray functionsgh-111140: Adds PyLong_CopyBits functionFeb 2, 2024
@zoobazooba changed the titlegh-111140: Adds PyLong_CopyBits functiongh-111140: Adds PyLong_CopyBits and PyLong_FromBits functionsFeb 2, 2024
@zoobazooba added the 🔨 test-with-buildbotsTest PR w/ buildbots; report in status section labelFeb 7, 2024
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by@zooba for commit6e1a89a 🤖

If you want to schedule another build, you need to add the🔨 test-with-buildbots label again.

@bedevere-botbedevere-bot removed the 🔨 test-with-buildbotsTest PR w/ buildbots; report in status section labelFeb 7, 2024
@zooba
Copy link
MemberAuthor

Thanks@erlend-aasland! Great feedback, and even found a bug (this is why we get reviews!)

I left one open question about doc wording, as I know you're more broadly involved in that than I am.

erlend-aasland reacted with heart emoji

Comment on lines 26 to 27
PyAPI_FUNC(int) PyLong_AsNativeBytes(PyObject* v, void* buffer, size_t n_bytes,
int endianness);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Shouldn't this returnsize_t rather thanint?

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Negative values are required, so we'd have to doPy_ssize_t and notsize_t. The cast is just as annoying either way unless we maken_bytes also signed, which is then inconsistent with other APIs (but probably less bad than making it acceptint).

We might need some agreed upon guidelines for choosing types for these kinds of purposes.int is a very common return type, and IMHO that makes things easier for people trying to call this from non-C languages, but they probably have no choice but to supportPy_ssize_t as a return type too and so it really wouldn't make a huge difference.

I pity the poor CPU that has to convert a 32 billion bit number 🙃

encukou reacted with thumbs up emoji
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I really need to focus on not mixing up signed/unsigned when I write a comment...

Yes,Py_ssize_t is one of the types that users need to support.
I'd much prefer usingPy_ssize_t for sizes. These are arbitrary-sized integers, after all; limited by available memory.

gpshead reacted with heart emoji
@encukou
Copy link
Member

encukou commentedFeb 9, 2024
edited
Loading

How should I usePyLong_AsNativeBytes if I have an existingsigned long I want to fill -- say, as part of a struct?
It seems that I'd need to allocate a buffer with one more byte, and then check that the extra byte is zero. And then memcpy to the destination.

@zooba
Copy link
MemberAuthor

How should I usePyLong_AsNativeBytes if I have an existingsigned long I want to fill -- say, as part of a struct?

n = PyLong_AsNativeBytes(v, &st.long_value, sizeof(long), -1);if (n < 0) {  // exception raised} else if (n <= sizeof(long)) {  // success} else {  // value overflows, but st.long_value is set correctly, just truncated}

What you do in the overflow case is up to you. Maybe you can overallocate? Maybe you just have to fail. But you don't have to overallocate to fit precisely into asigned variable.

For an unsigned variable, you may require one extra bit to store the sign bit, e.g.2**256-1 requires 257 bits (33 bytes) of storage, and so does-(2**256-1). The extra byte will contain all sign bits in both cases, but in the former we don't need it when we know the value is going to be treated as unsigned (the MSB of the 32-byte value is set in both cases).

There really isn't a consistent way for us to handle this, lacking knowledge of what the value actuallyis. The current (private) function just fails on all negative values if you ask for unsigned, but that doesn't actually touch this issue at all - it just excludes a whole lot of otherwise valid conversions (e.g.+/-(2**255-1) and smaller always fits in 32 bytes with no loss of data).

So the suggestion there is if youknow the magnitude of the value fits into your destination, or youdon't care, then ignore positive returns from the function. But again, it only affectspositive integers that require precisely the number of bits you've provided when treated as unsigned, and so treating as signed requires one extra.

erlend-aasland, encukou, and gpshead reacted with thumbs up emoji

Copy link
Member

@encukouencukou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Thanks.
IMO it's hard to implement a conversion to native unsigned integer on top of this, but, that could be solved by addingPyLong_AsNativeUnsignedBytes. No need to block this PR.

I trust your judgment onint/*size_t as well; no need for that to block the PR.

@zooba
Copy link
MemberAuthor

IMO it's hard to implement a conversion to native unsigned integer on top of this, but, that could be solved by addingPyLong_AsNativeUnsignedBytes. No need to block this PR.

I briefly implemented that. It took the size of the provided buffer, allocated a new one with one extra byte, did exactly the same thing, and copied the result into the original buffer or failed if it wasn't going to fit. It seems wasteful, especially compared to the caller doing a static allocation of a larger buffer themselves and calling this API, but we can't really improve on it that much - calculating that the resulting number is going to be in that one-bit worth of grey area isn't obvious. (And IMHOnot offering it makes it more likely that people will implement it efficiently for themselves, rather than complaining that we did it inefficiently.)

The thing thatisn't trivial is to reject negative values entirely, which someone may want to do. But that's more likely to be an application requirement than an integration/adapter requirement, so I'd expect they'll have a comparison to zero in their own language before they convert anyway, which means they'll go to a native signed integer rather than directly to unsigned, or they'll do the comparison using a Python comparison.

gpshead reacted with thumbs up emoji

@zoobazooba merged commit7861dfd intopython:mainFeb 12, 2024
@zooba
Copy link
MemberAuthor

I trust your judgment onint/*size_t as well; no need for that to block the PR.

@encukou It didn't block, I changed them all toPy_ssize_t 😄 My only concern was that comparing the result tosizeof(...) shouldn't cause a warning, but then I realised that the warning would also occur for (signed) int, so it doesn't matter.

@zoobazooba deleted the gh-111140 branchFebruary 12, 2024 20:14
@gpshead
Copy link
Member

additional follow on example and test improvements in#115380

@gpshead
Copy link
Member

Looking this API over the main thing I don't like is... unrelated to the API. It appears to be doing the right thing twos compliment wise given that it is returning data in whole bytes with the sign extension filling an entire additional byte when necessary as \xff or \x00.

I do find the need for an additional byte if someone is dealing exclusively with a value they already know to be non-negative yet large enough to occupy the high bit "annoying"... but this API isn't primarily for use by people wanting to merely fill a native type like that. They can add a range check of their own and be happy. ie: The prior understanding of the underlying value as discussed above.

Thus my PR making the example not use it to fill in a mereint to notencourage that pattern given native APIs for many types like that exist.

Granted we could change to encouraging this API everywhere in the future instead of people forgetting toif ((value = PyLong_AsLong(x)) == -1 && PyErr_Occurred()) { ... } being a super common flaw in code. This APImaybe more readily encourages checking the return value.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@scoderscoderscoder left review comments

@AlexWaygoodAlexWaygoodAlexWaygood left review comments

@encukouencukouencukou approved these changes

@erlend-aaslanderlend-aaslanderlend-aasland approved these changes

@corona10corona10Awaiting requested review from corona10corona10 is a code owner

@berkerpeksagberkerpeksagAwaiting requested review from berkerpeksagberkerpeksag is a code owner

Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

7 participants
@zooba@bedevere-bot@encukou@gpshead@scoder@erlend-aasland@AlexWaygood

[8]ページ先頭

©2009-2025 Movatter.jp