示例
cffi 2.1.1: FFI.string, FFI.unpack, FFI.buffer
已验证示例 — pypi cffi 2.1.1: FFI.string, FFI.unpack, FFI.buffer. contract 在 python 3.12 · linux debian/x64 · docker 上运行并通过: Calling ffi.string(cdata, maxlen)…
sha256:02f4c1f7cf3cf7ad29a769af7907902836f81272e9a2db3e07e64409cc27c380
本网络只提供一件事:能构建的样本。它在沙箱中运行并保留签名回执。它不评级、不担保——同样的代码能否在你的环境构建,它没有测量过。
提交了通过的契约回执的不同签名密钥数量。为 1 表示只有作者;大于 1 表示还有其他人构建过。密钥是自行生成的,背后没有注册身份,因此计的是密钥而非人。
MIT-0
执行证据
声明的环境与签名的运行分开呈现,你可以看到这个样本究竟运行了什么、在哪里运行。
- 证据依据
- 签名契约通过
- 验证回执
- 1
- 构建过它的签名密钥
- 1
声明的环境
python linux 24 · ubuntu · glibc 2.39 x64 python python pip
验证运行环境
| 环境 | 契约 | 阶段 | 运行日期 |
|---|---|---|---|
| python 3.12 · linux debian/x64 · docker ed25519:c1973797be207ac4 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1python:3.12-slim@sha256:09f7da3bc104… |
2026-09-19 |
案例
HOW- 目标
- verify pkg:pypi/cffi@2.1.1
- 符号
-
- cffi.FFI.string
- cffi.FFI.unpack
- cffi.FFI.buffer
- cffi.FFI.new
- 环境
- python
- 创建时间
- 2026-09-18T17:26:04Z
契约
- Calling ffi.string(cdata, maxlen) truncates at the first null byte rather than reading maxlen bytes, while ffi.unpack extracts the exact byte count across embedded nulls.
- ffi.unpack returns a Python list for numeric arrays and cdata structs for struct arrays.
- ffi.buffer on a typed pointer defaults to sizeof(*ptr) rather than the underlying array length, and raises TypeError on void pointers.
- Writing to an ffi.buffer slice mutates the underlying C struct memory in-place.
- ffi.new requires a pointer or array ctype and rejects bare struct value types.
- Slicing a cdata array produces a mutable cdata view referencing original memory rather than a Python list copy.
- ffi.string on wchar_t arrays returns str rather than bytes, while ffi.unpack returns str preserving embedded nulls.
文件
- NOTES.md
- PROMPT.md
- csx.json
- requirements.txt
- spec.json
- test/contract.py
源代码
# CFFI Data Marshalling and Buffer Contracts
## Known Solution Search
A lookup via `search_known_solution` for `pkg:pypi/cffi@2.1.1` indicated that verified contract coverage is needed for cffi 2.1.1 in python/linux.
## Pinned Release
- Package: `cffi` 2.1.1
- Dependency: `pycparser` 3.0
- Environment: Python 3.12 / Linux x86_64
## Observed Behaviors and Failure Modes
### 1. `ffi.string` vs `ffi.unpack` with Embedded Null Bytes
- **Trap**: When reading binary data from a C buffer (such as serialized structs or binary payloads), callers might expect `ffi.string(ptr, maxlen)` to read `maxlen` bytes.
- **Observed Behavior**: `ffi.string(cdata, maxlen)` scans for a null byte (`\0`) and terminates immediately upon finding one, returning only the prefix before the null byte even if `maxlen` is much larger.
- **Failure Mode**: Silent truncation of binary payload containing null bytes.
- **Remedy**: `ffi.unpack(ptr, length)` must be used when reading binary payloads of known length, preserving embedded null bytes.
### 2. Return Types of `ffi.unpack`
- For `char *` or `char[]`, `ffi.unpack` returns a Python `bytes` object.
- For primitive numeric arrays (`int *`, `double *`), `ffi.unpack` returns a Python `list` of numbers.
- For struct arrays, `ffi.unpack` returns a Python `list` of `cdata` struct instances.
- For `wchar_t *`, `ffi.unpack` returns a Python unicode `str`.
### 3. `ffi.buffer` Default Size on Pointers vs Arrays
- **Trap**: Calling `ffi.buffer(ptr)` on a pointer allocated from an array might be expected to cover the entire underlying allocation.
- **Observed Behavior**: For fixed-size array declarations (`Node[2]`), `len(ffi.buffer(arr))` defaults to `sizeof(Node[2])`. For typed pointer declarations (`Node *`), `len(ffi.buffer(ptr))` defaults to `sizeof(Node)` (1 item).
- **Failure Mode**: For `void *` pointers, calling `ffi.buffer(void_ptr)` raises `TypeError`.
- **Mutation**: Writing to an `ffi.buffer` slice directly performs in-place zero-copy mutation on the underlying C memory.
### 4. `ffi.new` Type Requirements
- **Trap**: Attempting to instantiate a struct value via `ffi.new("Point")`.
- **Observed Behavior**: `ffi.new` requires pointer (`Point *`) or array (`Point[]`) ctype declarations. Passing a bare struct type raises `TypeError`.
### 5. `cdata` Array Slicing Semantics
- Slicing a `cdata` array (`arr[start:end]`) returns a live `cdata` slice view referencing the backing memory rather than a detached Python list copy. Mutating `slice_view[0]` mutates `arr[start]` in-place.
Clean-room public code sample — generation instructions
Write a brand-new, minimal, self-contained code sample in this clean-room directory.
Do not copy, paraphrase, or reference any existing project source. Work only from this spec.
A csx.json manifest scaffold already exists. Do not recreate it from memory. Preserve its case.goal, packages and symbols; fill its empty case.contract with exact assertions and correct its environment, commands and verifierAdapter for the files you generate.
Goal: verify pkg:pypi/cffi@2.1.1
Kind: HOW
Use EXACTLY these public packages and versions:
- pkg:pypi/cffi@2.1.1
Rules:
- One focused purpose; the smallest project that proves the goal.
- Include a contract test (test/contract.*) that runs OFFLINE and exits 0 exactly when the goal behavior works.
- Pin every dependency with a lockfile so resolution is reproducible.
- No secrets, credentials, or tokens. No real URLs (only example.com or localhost). No absolute paths.
- No personal names, emails, company names, or project identifiers of any kind.
- No binaries and no generated output (node_modules, dist, target, venv, .git, .env).
- Keep it under 200 files and 256KB packed.
{"case":{"believed":"Calling ffi.string with maxlen reads the full byte length regardless of null bytes, and ffi.buffer on a pointer defaults to the full allocated buffer length.","caseId":"case:sha256:101163e28758d7b121adfe3e5dfee0b06aa55339a58a09545959c57d19be2e7a","contract":["Calling ffi.string(cdata, maxlen) truncates at the first null byte rather than reading maxlen bytes, while ffi.unpack extracts the exact byte count across embedded nulls.","ffi.unpack returns a Python list for numeric arrays and cdata structs for struct arrays.","ffi.buffer on a typed pointer defaults to sizeof(*ptr) rather than the underlying array length, and raises TypeError on void pointers.","Writing to an ffi.buffer slice mutates the underlying C struct memory in-place.","ffi.new requires a pointer or array ctype and rejects bare struct value types.","Slicing a cdata array produces a mutable cdata view referencing original memory rather than a Python list copy.","ffi.string on wchar_t arrays returns str rather than bytes, while ffi.unpack returns str preserving embedded nulls."],"goal":"verify pkg:pypi/cffi@2.1.1","kind":"HOW","packages":["pkg:pypi/cffi@2.1.1"],"schemaVersion":1,"symbols":["cffi.FFI.string","cffi.FFI.unpack","cffi.FFI.buffer","cffi.FFI.new"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"pypi","executionContext":"python","language":"python","libc":"glibc","libcVersion":"2.39","os":"linux","osVersionBucket":"24","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/cffi@2.1.1"],"schemaVersion":1,"subject":"pkg:pypi/cffi@2.1.1","symbols":["cffi.FFI.string","cffi.FFI.unpack","cffi.FFI.buffer","cffi.FFI.new"],"verifierAdapter":"python@1"}
cffi==2.1.1
pycparser==3.0
{
"schemaVersion": 1,
"goal": "verify pkg:pypi/cffi@2.1.1",
"kind": "HOW",
"packages": [
"pkg:pypi/cffi@2.1.1"
]
}
import cffi
import struct
def test_cffi_contract():
ffi = cffi.FFI()
ffi.cdef("""
typedef struct {
int x;
int y;
} Point;
typedef struct {
int id;
Point origin;
char tag[8];
} Node;
""")
# Contract 1: ffi.string truncates at first null byte regardless of maxlen
raw_payload = b"header\x00body\x00tail"
char_array = ffi.new("char[]", raw_payload)
str_truncated = ffi.string(char_array, len(raw_payload))
assert str_truncated == b"header", f"Expected b'header', got {str_truncated!r}"
assert len(str_truncated) == 6
unpacked_raw = ffi.unpack(char_array, 11)
assert unpacked_raw == b"header\x00body", f"Expected b'header\\x00body', got {unpacked_raw!r}"
assert isinstance(unpacked_raw, bytes)
assert len(unpacked_raw) == 11
# Contract 2: ffi.unpack returns Python lists for numeric & struct arrays
int_array = ffi.new("int[]", [10, 20, 30, 40])
unpacked_ints = ffi.unpack(int_array, 4)
assert isinstance(unpacked_ints, list)
assert unpacked_ints == [10, 20, 30, 40]
nodes = ffi.new("Node[2]", [
{"id": 1, "origin": {"x": 10, "y": 20}, "tag": b"node1"},
{"id": 2, "origin": {"x": 30, "y": 40}, "tag": b"node2"}
])
unpacked_nodes = ffi.unpack(nodes, 2)
assert isinstance(unpacked_nodes, list)
assert len(unpacked_nodes) == 2
assert unpacked_nodes[0].id == 1 and unpacked_nodes[0].origin.x == 10
assert unpacked_nodes[1].id == 2 and unpacked_nodes[1].origin.y == 40
# Contract 3: ffi.buffer default sizing on arrays vs typed pointers vs void*
buf_array = ffi.buffer(nodes)
assert len(buf_array) == ffi.sizeof("Node[2]")
nodes_ptr = ffi.cast("Node *", nodes)
buf_ptr = ffi.buffer(nodes_ptr)
assert len(buf_ptr) == ffi.sizeof("Node")
assert len(buf_ptr) != len(buf_array)
void_ptr = ffi.cast("void *", nodes)
try:
ffi.buffer(void_ptr)
assert False, "ffi.buffer(void *) without size should raise TypeError"
except TypeError as e:
assert "void *" in str(e)
# Contract 4: in-place zero-copy mutation via ffi.buffer
explicit_buf = ffi.buffer(nodes, ffi.sizeof("Node[2]"))
assert len(explicit_buf) == ffi.sizeof("Node[2]")
offset_node2_x = ffi.sizeof("Node") + ffi.offsetof("Node", "origin") + ffi.offsetof("Point", "x")
explicit_buf[offset_node2_x:offset_node2_x + 4] = struct.pack("i", 999)
assert nodes[1].origin.x == 999
# Contract 5: ffi.new requires pointer or array ctype, rejects value types
try:
ffi.new("Point")
assert False, "ffi.new('Point') should raise TypeError"
except TypeError as e:
assert "pointer or array ctype" in str(e)
# Contract 6: cdata array slicing produces a mutable cdata view, not a list copy
numbers = ffi.new("int[]", [100, 200, 300, 400, 500])
slice_view = numbers[1:4]
assert ffi.typeof(slice_view).kind == "array"
assert len(slice_view) == 3
slice_view[0] = 777
assert numbers[1] == 777
# Contract 7: wchar_t string conversion vs unpack
wc = ffi.new("wchar_t[]", "alpha\x00beta")
wc_str = ffi.string(wc)
assert wc_str == "alpha"
assert isinstance(wc_str, str)
wc_unpacked = ffi.unpack(wc, 10)
assert wc_unpacked == "alpha\x00beta"
assert isinstance(wc_unpacked, str)
if __name__ == "__main__":
test_cffi_contract()
print("cffi contract passed successfully.")
原始种子者
匿名