int.rs
Overview
The `int.rs` file provides functionality to serialize Python integer objects (`PyLong`) into Rust's serialization framework using Serde. It defines the `IntSerializer` struct, which wraps a raw pointer to a Python integer object and an options struct (`Opt`) to control serialization behavior. The main purpose of this file is to convert Python integer values into Rust's primitive integer types (`i64` or `u64`) for serialization, while respecting certain constraints such as integer size limits and strictness options aligned with JSON number specifications (RFC 7159).
Key features include:
Support for serializing both signed and unsigned Python integers.
Handling of integers that fit within 32 or 64 bits.
Optional strict mode enforcement limiting integers to the safe JavaScript integer range (±2^53 - 1).
Compatibility with different Python versions (conditional compilation for Python 3.13+).
Integration with Serde's
Serializetrait to allow Python integers to be serialized into various output formats (e.g., JSON, MessagePack).
Entities
Constants
Name | Type | Description |
|---|---|---|
`STRICT_INT_MIN` | i64 | Minimum allowed integer in strict mode, `-(2^53)+1` as per RFC 7159 JSON integer safe range. |
`STRICT_INT_MAX` | i64 | Maximum allowed integer in strict mode, `(2^53)-1` as per RFC 7159 JSON integer safe range. |
Structs
IntSerializer
A serializer wrapper for Python integer objects.
Fields:
ptr: *mut pyo3_ffi::PyObject
Raw pointer to the Python integer object.opts: Opt
Serialization options controlling behaviors such as strict integer range enforcement.
Purpose:
Encapsulates the Python integer and options, providing aSerializeimplementation to convert the integer into Rust primitive integer types for serialization.
Detailed Explanation
IntSerializer Struct
pub(crate) struct IntSerializer {
ptr: *mut pyo3_ffi::PyObject,
opts: Opt,
}
Usage:
Represents a Python integer with associated options for serialization.Parameters:
ptr: Pointer to the Python integer object (PyLong).opts: Options controlling serialization behavior.
Instantiation:
impl IntSerializer {
pub fn new(ptr: *mut pyo3_ffi::PyObject, opts: Opt) -> Self {
IntSerializer {
ptr,
opts,
}
}
}
Creates a new
IntSerializergiven a Python integer pointer and options.
Serialize Trait Implementation for IntSerializer
Provides the core logic to convert the Python integer into a serializable Rust integer.
There are two variants of the `serialize` method, selected at compile time via the `inline_int` feature flag.
1. serialize with inline_int feature enabled
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
Logic Summary:
Checks if the Python integer equals zero using
pylong_is_zero.Determines if the integer is signed or unsigned.
If the integer fits within 32-bit signed/unsigned integer:
Serializes directly using
serialize_u64orserialize_i64.
Otherwise:
Uses
_PyLong_AsByteArrayto extract the integer as an 8-byte buffer.Converts the buffer to
u64ori64.Checks strict integer bounds if
STRICT_INTEGERoption is enabled.Serializes the integer.
Handles errors such as inability to convert or out-of-range values.
Parameters:
serializer: A Serde serializer implementing theSerializertrait.
Returns:
Result<S::Ok, S::Error>: Result of serialization or an error.
Error Cases:
Fails with
SerializeError::Integer64Bitsif the integer cannot be represented in 64 bits.Fails with
SerializeError::Integer53Bitsif strict mode is enabled and integer exceeds safe JavaScript integer range.
Example Usage:
let py_int_ptr: *mut pyo3_ffi::PyObject = ...; // obtained from Python environment
let options = Opt::default();
let serializer = IntSerializer::new(py_int_ptr, options);
// Assuming `json_serializer` implements serde::Serializer
let result = serializer.serialize(json_serializer);
match result {
Ok(serialized) => println!("Serialized integer successfully"),
Err(e) => eprintln!("Serialization error: {:?}", e),
}
2. serialize without inline_int feature
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
Logic Summary:
Checks if the Python integer is unsigned or signed.
Uses Python C API calls
PyLong_AsUnsignedLongLongorPyLong_AsLongLongto convert the Python integer to Rust'su64ori64.Checks for errors during conversion (e.g., overflow).
Enforces strict integer range if enabled.
Serializes the integer using the appropriate serializer method.
Differences from the
inline_intversion:Does not use inline access or
_PyLong_AsByteArray.Relies on Python C API functions for conversion.
Important Implementation Details & Algorithms
Use of Unsafe Code:
The file extensively usesunsafeblocks to interact with Python's C API directly via raw pointers (*mut PyObject). This is necessary because Python objects are managed externally, and Rust must ensure soundness manually.Integer Size and Sign Determination:
The code queries the Python integer object to determine if it fits in 32 bits or requires 64-bit representation, and whether it is signed or unsigned. This guides serialization to use the most efficient representation.Strict Integer Range Enforcement:
When theSTRICT_INTEGERoption is enabled (likely for JSON compatibility), integers are restricted to the range ±(2^53 - 1). This ensures serialized numbers conform to JavaScript's safe integer range, preventing precision loss.Conditional Compilation for Python Versions:
The code adapts the call to_PyLong_AsByteArraydepending on Python version, supporting compatibility from pre-3.13 and 3.13+.Error Handling:
Errors from the Python C API are checked and cleared as necessary, and mapped intoSerializeErrorvariants that the serializer can propagate.
Interaction with Other System Components
Optand Serialization Options:
TheIntSerializeraccepts anOptstruct, which controls options such asSTRICT_INTEGERthat influence serialization behavior.SerializeError:
Errors during serialization are reported using theSerializeErrorenum, likely defined elsewhere in the crate underserialize::error.Python C API (
pyo3_ffi):
The file relies on thepyo3_ffimodule to interact with Python's internal C API for integer inspection and conversion.Serde Framework:
Implements theserde::ser::Serializetrait, allowing the serialized integers to be consumed by any Serde-compatible serializer (e.g., JSON, CBOR, MessagePack).
Visual Diagram: Structure Class Diagram
classDiagram
class IntSerializer {
-ptr: *mut PyObject
-opts: Opt
+new(ptr: *mut PyObject, opts: Opt) IntSerializer
+serialize<S: Serializer>(serializer: S) -> Result<S::Ok, S::Error>
}
IntSerializer ..> Opt : uses
IntSerializer ..|> Serialize
The diagram shows the
IntSerializerstruct with private fieldsptrandopts.IntSerializerimplements theSerializetrait.It depends on the
Optstruct for configuration.
Summary
The `int.rs` file is a focused utility module that bridges Python integer objects with Rust's serialization ecosystem. It carefully manages the conversion of Python's arbitrary precision integers into Rust's primitive integer types, ensuring safety, correctness, and compliance with serialization constraints. It is a critical component when serializing Python data structures into formats like JSON or other serialized representations within this project.
References
RFC 7159 Section 6 — JSON Number specification.
Serde Rust Library — Serialization framework used.
PyO3 FFI — Rust bindings for Python C API.