You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/allocation.md
+96Lines changed: 96 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -98,3 +98,99 @@ for index, ptr in enumerate(array):
98
98
99
99
print(ptr[1]) # prints out "1"
100
100
```
101
+
102
+
## Stack
103
+
104
+
Objects can be put on the stack using `stack_alloc` or `acquire_stack_alloc`:
105
+
106
+
```py
107
+
from pointers import stack_alloc, StackAllocatedPointer
108
+
109
+
@stack_alloc(28)
110
+
deftest(ptr: StackAllocatedPointer):
111
+
ptr <<=1
112
+
print(*ptr)
113
+
114
+
test()
115
+
```
116
+
117
+
The difference between `acquire_stack_alloc` and `stack_alloc` is that `acquire_stack_alloc` automatically calls the function, whereas `stack_alloc` makes you call it manually:
118
+
119
+
```py
120
+
from pointers import stack_alloc, acquire_stack_alloc, StackAllocatedPointer
121
+
122
+
@stack_alloc(28)
123
+
defa(ptr: StackAllocatedPointer): # you need to call a manually
124
+
...
125
+
126
+
@acquire_stack_alloc(28)
127
+
defb(ptr: StackAllocatedPointer): # this is called automatically by the decorator
128
+
...
129
+
```
130
+
131
+
Stack allocated pointers **cannot** be deallocated manually, meaning the following **will not** work:
132
+
133
+
```py
134
+
from pointers import acquire_stack_alloc, StackAllocatedPointer, free
135
+
136
+
@acquire_stack_alloc(28)
137
+
deftest(ptr: StackAllocatedPointer):
138
+
free(ptr) # ValueError is raised
139
+
```
140
+
141
+
Instead, it will be popped off the stack at the end of the function. Read more about that below.
142
+
143
+
### Why not a context?
144
+
145
+
**Warning:** This section is for advanced users.
146
+
147
+
We have to go through a couple different reasons on why we need to use a decorator instead of a context for stack allocations.
148
+
149
+
First, we need to understand how functions are handled in ASM (at least on GNU compilers).
150
+
151
+
Lets take a look at this piece of x86 ASM as an example (32 bit):
152
+
153
+
```asm
154
+
global _start
155
+
156
+
_start:
157
+
push 123 ; 123 on stack
158
+
call func ; call our function
159
+
mov eax, 1 ; system exit
160
+
mov ebx, 0 ; return code
161
+
int 0x80 ; call kernel
162
+
163
+
func:
164
+
push ebp ; push base pointer onto the stack
165
+
mov ebp, esp ; preserve current stack top
166
+
167
+
mov esp, ebp
168
+
pop ebp ; restore base pointer
169
+
ret ; jump to return address
170
+
```
171
+
172
+
This function does nothing, but as you can see with the `push` and `pop` instructions, we are using the stack to pass parameters and store the return address of the functions.
173
+
174
+
When we put something on the top of the stack in Python, we still need to follow these rules. The memory is popped off the stack at the end of the C function (in this case, its pointers.py's `run_stack_callback`), so we cannot use it elsewhere.
175
+
176
+
Now, how does this relate to using a context or not?
177
+
178
+
Python context managers are done like this:
179
+
180
+
```py
181
+
classMyContext:
182
+
def__enter__(self):
183
+
...
184
+
185
+
def__exit__(self, *_):
186
+
...
187
+
188
+
with MyContext() as x:
189
+
...
190
+
```
191
+
192
+
Remember, the data is popped off the stack after the end of the C function, not the Python function, meaning we can't just allocate and then store in the class. We also can't just do it all from `__enter__`, since the allocated memory will be destroyed at the end of it.
193
+
194
+
We also can't do it from a C based class, since that will still have to return the object.
195
+
196
+
Note that there's also no yielding in C, so we can't return early either.
Copy file name to clipboardExpand all lines: docs/bindings.md
+34Lines changed: 34 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -151,6 +151,40 @@ signal(2, sighandler)
151
151
c_raise(2) # send signal 2 to the program
152
152
```
153
153
154
+
Alternatively, you can create a function pointer using `to_func_ptr`:
155
+
156
+
```py
157
+
from pointers import to_func_ptr, signal, exit, c_raise
158
+
159
+
deftest(signum: int) -> None:
160
+
print('hello world')
161
+
exit(0)
162
+
163
+
signal(2, to_func_ptr(test))
164
+
c_raise(2)
165
+
```
166
+
167
+
**Note:**`to_func_ptr` requires the function to be type hinted in order to correctly generate the signature of the C function, so the following **will not** work:
168
+
169
+
```py
170
+
deftest(signum):
171
+
...
172
+
173
+
to_func_ptr(test)
174
+
```
175
+
176
+
## Null Pointers
177
+
178
+
If you would like to pass a `NULL` pointer to a binding, you can use `pointers.NULL` or pass `None`:
179
+
180
+
```py
181
+
from pointers import time, NULL
182
+
183
+
# these two do the same thing
184
+
print(time(NULL))
185
+
print(time(None))
186
+
```
187
+
154
188
## Custom Bindings
155
189
156
190
You can create your own binding to a C function with `ctypes` and pointers.py.
0 commit comments