Skip to content
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

bpo-32780: Fix the PEP3118 format string for ctypes.Structure #5561

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

eric-wieser
Copy link
Contributor

@eric-wieser eric-wieser commented Feb 6, 2018

The summary of this diff is that it:

  • adds a _ctypes_alloc_format_padding function to append strings like 37x to a format string to indicate 37 padding bytes
  • removes the branches that amount to "give up on producing a valid format string if the struct is packed"
  • combines the resulting adjacent if (isStruct) {s now that neither is if (isStruct && !isPacked) {
  • invokes _ctypes_alloc_format_padding to add padding between structure fields, and after the last structure field. The computation used for the total size is unchanged from ctypes already used.

This patch does not affect any existing aligment computation; all it does is use subtraction to deduce the amount of paddnig introduced by the existing code.


Without this fix, it would never include padding bytes - an assumption that was only
valid in the case when _pack_ was set - and this case was explicitly not implemented.

This should allow conversion from ctypes structs to numpy structs

Fixes numpy/numpy#10528

https://bugs.python.org/issue32780

(#76961)

@the-knights-who-say-ni
Copy link

the-knights-who-say-ni commented Feb 6, 2018

Hello, and thanks for your contribution!

I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA).

Unfortunately our records indicate you have not signed the CLA. For legal reasons we need you to sign this before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue.

Thanks again to your contribution and we look forward to looking at it!

Without this fix, it would never include padding bytes - an assumption that was only
valid in the case when `_pack_` was set - and this case was explicitly not implemented.

This should allow conversion from ctypes structs to numpy structs.
@eric-wieser

This comment has been minimized.

mattip
mattip approved these changes May 23, 2018
Copy link
Contributor

@mattip mattip left a comment

LGTM. The PR is sufficient to fix the problems described. Perhaps judicious use of spacing between fields could make the format clearer, especially when anonymous padding is added. For instance, I would find "<b:x:7x<Q:y:" clearer as "<b:x: 7x <Q:y:". Not critical as the format is probably going to be machine-parsed anyway.

@eric-wieser
Copy link
Contributor Author

eric-wieser commented May 23, 2018

That's looks like a reasonable suggestion, bit possibly out of scope for this PR.

Less intrusively, I could add whitespace in the test expectations, and remove it before comparing.

Let's see how the core devs feel.

Thanks for the review!

@eric-wieser
Copy link
Contributor Author

eric-wieser commented Oct 21, 2018

@abalkin: Any chance you could take a look at this?

@brettcannon brettcannon added the type-feature A feature request or enhancement label Apr 2, 2019
@eric-wieser
Copy link
Contributor Author

eric-wieser commented Apr 14, 2019

@brettcannon: I'd argue this is type-bugfix, not type-enhancement - the implementation in master produces invalid buffers in almost all cases. This isn't just adding support for _pack_ (an enhancement), but also fixing the behavior for when _pack_ is absent.

@brettcannon brettcannon added type-bug An unexpected behavior, bug, or error and removed type-feature A feature request or enhancement labels Apr 17, 2019
@mattip
Copy link
Contributor

mattip commented Sep 23, 2019

ping

@eric-wieser
Copy link
Contributor Author

eric-wieser commented Nov 28, 2019

Resolved merge conflicts with https://bugs.python.org/issue22273

@mattip
Copy link
Contributor

mattip commented May 24, 2020

Is there a buffer protocol expert who could help move this along?

@github-actions
Copy link

github-actions bot commented Aug 14, 2022

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Aug 14, 2022
@mattip
Copy link
Contributor

mattip commented Aug 14, 2022

How can we move this 4-year old PR forward?

Py_ssize_t log_n = 0;
while (n > 0) {
log_n++;
n /= 10;
Copy link
Member

@abalkin abalkin Aug 14, 2022

Choose a reason for hiding this comment

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

Multiplication is often faster than division. Can this be rewritten by computing powers of 10 until n is exceeded?

Better yet, just inline linear search.

   if (n < 10ULL)
       return 1;
   if (n < 100ULL)
       return 2;
    ...
   if (n < 10_000_000_000_000_000_000ULL)
       return 20; 

Copy link
Member

@abalkin abalkin Aug 14, 2022

Choose a reason for hiding this comment

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

On the second thought, I would be surprised if this has not been implemented elsewhere in cpython code base. Off the top of my head, I cannot recall where it could be, but I will try to search. If someone beats me to it - please leave a note.

Copy link
Contributor Author

@eric-wieser eric-wieser Aug 15, 2022

Choose a reason for hiding this comment

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

Can this be rewritten by computing powers of 10 until n is exceeded?

This is risky because the power can overflow

}

/* decimal characters + x + null */
buf = PyMem_Malloc(clog10(padding) + 2);
Copy link
Member

@abalkin abalkin Aug 14, 2022

Choose a reason for hiding this comment

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

I left a comment about log10 implementation above, but looking at the actual use, I don't see why it is needed. Can't we just make buf

char buf[20];

and not allocate it on the heap?

Copy link
Contributor Author

@eric-wieser eric-wieser Aug 15, 2022

Choose a reason for hiding this comment

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

I wanted to avoid risking introducing a buffer overflow by accident by choosing too short a buffer; especially since I don't think we care about performance here. The whole framework for building the format strings here consists of repeated heap allocations, so one more allocation doesn't seem like a big deal.

I think 20 isn't actually enough, as an int64 can need up to 19 digits, and then we need the x and the null.

I could ask PyOS_snprintf to compute the size for me if you'd prefer? Although I can't see any evidence that PyOS_snprintf is actually called with a null buffer anywhere in CPython. Nevermind, PyOS_snprintf does not support this feature of snprintf (#95993)

Copy link
Contributor Author

@eric-wieser eric-wieser Aug 15, 2022

Choose a reason for hiding this comment

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

I've pushed the version with stack allocation as requested

@github-actions github-actions bot removed the stale Stale PR or inactive for long period of time. label Aug 15, 2022
@eric-wieser eric-wieser requested review from abalkin and removed request for skrah Oct 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting core review type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BUG: Cannot convert ctypes struct into numpy array
8 participants