Skip to content

Commit 8280c98

Browse files
Merge sedsprintf_rs upstream main
2 parents dfd67eb + 349feec commit 8280c98

File tree

11 files changed

+877
-95
lines changed

11 files changed

+877
-95
lines changed

sedsprintf_rs/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
2+
version = "1.1.0"
23
name = "sedsprintf_rs_2026"
3-
version = "1.0.8"
44
edition = "2024"
55
build = "build.rs"
66
authors = ["Rylan Meilutis <[email protected]>"]

sedsprintf_rs/header_templates/sedsprintf.h.txt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <stdint.h>
77
#include <stddef.h>
88
#include <string.h> /* for strlen in string macros */
9+
#include <stdbool.h> /* for bool type */
910

1011
#ifdef __cplusplus
1112
extern "C" {
@@ -179,6 +180,57 @@ int32_t seds_pkt_copy_bytes(const SedsPacketView * pkt, void * dst, size_t dst_l
179180

180181
int32_t seds_pkt_copy_data(const SedsPacketView * pkt, size_t elem_size, void * dst, size_t dst_elems);
181182

183+
184+
/**
185+
* Typed payload getters using TelemetryPacket::data_as_* helpers.
186+
*
187+
* Semantics (matching seds_pkt_copy_data / Rust side):
188+
* - If out is NULL or out_elems == 0 or out_elems < needed:
189+
* returns the required element count (>= 0), performs NO copy.
190+
* - On success:
191+
* returns the number of elements written (>= 0).
192+
* - On error:
193+
* returns a negative SedsResult error code.
194+
*/
195+
196+
int32_t seds_pkt_get_f32 (const SedsPacketView * pkt, float * out, size_t out_elems);
197+
int32_t seds_pkt_get_f64 (const SedsPacketView * pkt, double * out, size_t out_elems);
198+
199+
int32_t seds_pkt_get_u8 (const SedsPacketView * pkt, uint8_t * out, size_t out_elems);
200+
int32_t seds_pkt_get_u16 (const SedsPacketView * pkt, uint16_t * out, size_t out_elems);
201+
int32_t seds_pkt_get_u32 (const SedsPacketView * pkt, uint32_t * out, size_t out_elems);
202+
int32_t seds_pkt_get_u64 (const SedsPacketView * pkt, uint64_t * out, size_t out_elems);
203+
204+
int32_t seds_pkt_get_i8 (const SedsPacketView * pkt, int8_t * out, size_t out_elems);
205+
int32_t seds_pkt_get_i16 (const SedsPacketView * pkt, int16_t * out, size_t out_elems);
206+
int32_t seds_pkt_get_i32 (const SedsPacketView * pkt, int32_t * out, size_t out_elems);
207+
int32_t seds_pkt_get_i64 (const SedsPacketView * pkt, int64_t * out, size_t out_elems);
208+
209+
int32_t seds_pkt_get_bool(const SedsPacketView * pkt, bool * out, size_t out_elems);
210+
211+
/**
212+
* String getter for MessageDataType::String payloads.
213+
*
214+
* Semantics:
215+
* - If buf is NULL or buf_len == 0:
216+
* returns required length INCLUDING terminating NUL (>= 1), no copy.
217+
* - If buf_len is too small:
218+
* writes as much as fits (buf_len-1 chars), NUL-terminates,
219+
* returns required length (>= 1).
220+
* - On success:
221+
* returns 0.
222+
* - On error:
223+
* returns negative SedsResult error code.
224+
*/
225+
int32_t seds_pkt_get_string(const SedsPacketView * pkt, char * buf, size_t buf_len);
226+
227+
/* @breif Get the length of a string payload (Including NUL terminator).
228+
* @return length (>=0) on success; SedsResult on error.
229+
*/
230+
int32_t seds_pkt_get_string_len(const SedsPacketView * pkt);
231+
232+
233+
182234
SedsResult seds_pkt_get_typed(const SedsPacketView * pkt,
183235
void * out,
184236
size_t count,

sedsprintf_rs/header_templates/sedsprintf_rs.pyi.txt

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,23 @@ class Packet:
7878
def wire_size(self) -> int: ...
7979
def serialize(self) -> bytes: ...
8080

81+
# Typed payload decoders (mirror TelemetryPacket::data_as_*)
82+
def data_as_u8(self) -> List[int]: ...
83+
def data_as_u16(self) -> List[int]: ...
84+
def data_as_u32(self) -> List[int]: ...
85+
def data_as_u64(self) -> List[int]: ...
86+
87+
def data_as_i8(self) -> List[int]: ...
88+
def data_as_i16(self) -> List[int]: ...
89+
def data_as_i32(self) -> List[int]: ...
90+
def data_as_i64(self) -> List[int]: ...
91+
92+
def data_as_f32(self) -> List[float]: ...
93+
def data_as_f64(self) -> List[float]: ...
94+
95+
def data_as_bool(self) -> List[bool]: ...
96+
def data_as_string(self) -> str: ...
97+
8198

8299
# ==============================
83100
# Router (lifecycle, logging, queues)
@@ -97,12 +114,13 @@ class Router:
97114
"""
98115

99116
@staticmethod
100-
def new_singleton(tx: Optional[TxCallback] = ...,
101-
now_ms: Optional[NowMsCallback] = ...,
102-
handlers: Optional[
103-
Sequence[Tuple[int, Optional[PacketHandler], Optional[SerializedHandler]]]
104-
] = ...,
105-
) -> "Router":
117+
def new_singleton(
118+
tx: Optional[TxCallback] = ...,
119+
now_ms: Optional[NowMsCallback] = ...,
120+
handlers: Optional[
121+
Sequence[Tuple[int, Optional[PacketHandler], Optional[SerializedHandler]]]
122+
] = ...,
123+
) -> "Router":
106124
"""
107125
Create or retrieve a per-process singleton Router.
108126

@@ -115,7 +133,6 @@ class Router:
115133
"""
116134
...
117135

118-
119136
def __init__(
120137
self,
121138
tx: Optional[TxCallback] = ...,

sedsprintf_rs/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "maturin"
44

55
[project]
66
name = "sedsprintf_rs_2026"
7-
version = "1.0.8"
7+
version = "1.1.0"
88
description = "Rust telemetry and serialization library"
99
authors = [{ name = "Rylan Meilutis", email = "[email protected]" }]
1010
readme = "README.md"

sedsprintf_rs/src/c_api.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,123 @@ pub extern "C" fn seds_pkt_data_ptr(
11361136
view.payload as *const c_void
11371137
}
11381138

1139+
1140+
macro_rules! impl_seds_pkt_get_typed_from_packet {
1141+
($fname:ident, $method:ident, $ty:ty) => {
1142+
#[unsafe(no_mangle)]
1143+
pub extern "C" fn $fname(
1144+
pkt: *const SedsPacketView,
1145+
out: *mut $ty,
1146+
out_elems: usize,
1147+
) -> i32 {
1148+
// Basic arg check
1149+
if pkt.is_null() {
1150+
return status_from_err(TelemetryError::BadArg);
1151+
}
1152+
1153+
let view = unsafe { &*pkt };
1154+
let tpkt = match view_to_packet(view) {
1155+
Ok(p) => p,
1156+
Err(_) => return status_from_err(TelemetryError::BadArg),
1157+
};
1158+
1159+
let vals = match tpkt.$method() {
1160+
Ok(v) => v,
1161+
Err(e) => return status_from_err(e),
1162+
};
1163+
1164+
let needed = vals.len();
1165+
1166+
// No payload => nothing to copy
1167+
if needed == 0 {
1168+
return 0;
1169+
}
1170+
1171+
// Query / too-small: return required element count
1172+
if out.is_null() || out_elems == 0 || out_elems < needed {
1173+
return needed as i32;
1174+
}
1175+
1176+
unsafe {
1177+
ptr::copy_nonoverlapping(vals.as_ptr(), out, needed);
1178+
}
1179+
1180+
needed as i32
1181+
}
1182+
};
1183+
}
1184+
1185+
1186+
// Typed getters using TelemetryPacket's data_as_* helpers.
1187+
// All use "query" semantics like seds_pkt_copy_data:
1188+
// - If out is NULL or out_elems < needed, return needed (element count) and do not copy.
1189+
// - On success, return the number of elements written.
1190+
1191+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_f32, data_as_f32, f32);
1192+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_f64, data_as_f64, f64);
1193+
1194+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_u8, data_as_u8, u8);
1195+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_u16, data_as_u16, u16);
1196+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_u32, data_as_u32, u32);
1197+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_u64, data_as_u64, u64);
1198+
1199+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_i8, data_as_i8, i8);
1200+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_i16, data_as_i16, i16);
1201+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_i32, data_as_i32, i32);
1202+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_i64, data_as_i64, i64);
1203+
1204+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_bool, data_as_bool, bool);
1205+
1206+
#[unsafe(no_mangle)]
1207+
pub extern "C" fn seds_pkt_get_string(
1208+
pkt: *const SedsPacketView,
1209+
buf: *mut c_char,
1210+
buf_len: usize,
1211+
) -> i32 {
1212+
if pkt.is_null() {
1213+
return status_from_err(TelemetryError::BadArg);
1214+
}
1215+
1216+
let view = unsafe { &*pkt };
1217+
let tpkt = match view_to_packet(view) {
1218+
Ok(p) => p,
1219+
Err(_) => return status_from_err(TelemetryError::BadArg),
1220+
};
1221+
1222+
let s = match tpkt.data_as_string() {
1223+
Ok(s) => s,
1224+
Err(e) => return status_from_err(e),
1225+
};
1226+
1227+
// Same semantics as the other string helpers:
1228+
// - if buf is NULL or buf_len == 0 → return required length (incl NUL)
1229+
// - if too small → write as much as fits + NUL, return required length
1230+
// - on success → return 0
1231+
unsafe { write_str_to_buf(&s, buf, buf_len) }
1232+
}
1233+
1234+
#[unsafe(no_mangle)]
1235+
pub extern "C" fn seds_pkt_get_string_len(
1236+
pkt: *const SedsPacketView,
1237+
) -> i32 {
1238+
if pkt.is_null() {
1239+
return status_from_err(TelemetryError::BadArg);
1240+
}
1241+
1242+
let view = unsafe { &*pkt };
1243+
let tpkt = match view_to_packet(view) {
1244+
Ok(p) => p,
1245+
Err(_) => return status_from_err(TelemetryError::BadArg),
1246+
};
1247+
1248+
let s = match tpkt.data_as_string() {
1249+
Ok(s) => s,
1250+
Err(e) => return status_from_err(e),
1251+
};
1252+
1253+
(s.len() + 1) as i32
1254+
}
1255+
11391256
/// Copy raw payload bytes into `dst`.
11401257
///
11411258
/// Query mode:

sedsprintf_rs/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,9 @@ pub enum TelemetryError {
355355

356356
/// UTF-8 decoding failed where string payloads are expected.
357357
InvalidUtf8,
358+
359+
/// Payload type size mismatch.
360+
TypeMismatch { expected: usize, got: usize },
358361
}
359362

360363
impl TelemetryError {
@@ -375,6 +378,7 @@ impl TelemetryError {
375378
TelemetryError::Deserialize(_) => TelemetryErrorCode::Deserialize,
376379
TelemetryError::Io(_) => TelemetryErrorCode::Io,
377380
TelemetryError::InvalidUtf8 => TelemetryErrorCode::InvalidUtf8,
381+
TelemetryError::TypeMismatch { .. } => TelemetryErrorCode::TypeMismatch,
378382
}
379383
}
380384
}
@@ -433,6 +437,7 @@ pub enum TelemetryErrorCode {
433437
Deserialize = -12,
434438
Io = -13,
435439
InvalidUtf8 = -14,
440+
TypeMismatch = -15,
436441
}
437442

438443
// Generate ReprI32Enum helpers for TelemetryErrorCode
@@ -468,6 +473,7 @@ impl TelemetryErrorCode {
468473
TelemetryErrorCode::Deserialize => "{Deserialize Error}",
469474
TelemetryErrorCode::Io => "{IO Error}",
470475
TelemetryErrorCode::InvalidUtf8 => "{Invalid UTF-8}",
476+
TelemetryErrorCode::TypeMismatch => "{Type Mismatch}",
471477
}
472478
}
473479

sedsprintf_rs/src/python_api.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,105 @@ impl PyPacket {
153153
PyBytes::new(py, &self.inner.payload())
154154
}
155155

156+
/// Decode payload as `Vec<u8>` for unsigned byte arrays.
157+
fn data_as_u8(&self) -> PyResult<Vec<u8>> {
158+
self.inner
159+
.data_as_u8()
160+
.map(|v| v.to_vec())
161+
.map_err(py_err_from)
162+
}
163+
164+
/// Decode payload as `Vec<u16>` for unsigned 16-bit arrays.
165+
fn data_as_u16(&self) -> PyResult<Vec<u16>> {
166+
self.inner
167+
.data_as_u16()
168+
.map(|v| v.to_vec())
169+
.map_err(py_err_from)
170+
}
171+
172+
/// Decode payload as `Vec<u32>` for unsigned 32-bit arrays.
173+
fn data_as_u32(&self) -> PyResult<Vec<u32>> {
174+
self.inner
175+
.data_as_u32()
176+
.map(|v| v.to_vec())
177+
.map_err(py_err_from)
178+
}
179+
180+
/// Decode payload as `Vec<u64>` for unsigned 64-bit arrays.
181+
fn data_as_u64(&self) -> PyResult<Vec<u64>> {
182+
self.inner
183+
.data_as_u64()
184+
.map(|v| v.to_vec())
185+
.map_err(py_err_from)
186+
}
187+
188+
/// Decode payload as `Vec<i8>` for signed 8-bit arrays.
189+
fn data_as_i8(&self) -> PyResult<Vec<i8>> {
190+
self.inner
191+
.data_as_i8()
192+
.map(|v| v.to_vec())
193+
.map_err(py_err_from)
194+
}
195+
196+
/// Decode payload as `Vec<i16>` for signed 16-bit arrays.
197+
fn data_as_i16(&self) -> PyResult<Vec<i16>> {
198+
self.inner
199+
.data_as_i16()
200+
.map(|v| v.to_vec())
201+
.map_err(py_err_from)
202+
}
203+
204+
/// Decode payload as `Vec<i32>` for signed 32-bit arrays.
205+
fn data_as_i32(&self) -> PyResult<Vec<i32>> {
206+
self.inner
207+
.data_as_i32()
208+
.map(|v| v.to_vec())
209+
.map_err(py_err_from)
210+
}
211+
212+
/// Decode payload as `Vec<i64>` for signed 64-bit arrays.
213+
fn data_as_i64(&self) -> PyResult<Vec<i64>> {
214+
self.inner
215+
.data_as_i64()
216+
.map(|v| v.to_vec())
217+
.map_err(py_err_from)
218+
}
219+
220+
/// Decode payload as `Vec<f32>` for float32 arrays.
221+
fn data_as_f32(&self) -> PyResult<Vec<f32>> {
222+
self.inner
223+
.data_as_f32()
224+
.map(|v| v.to_vec())
225+
.map_err(py_err_from)
226+
}
227+
228+
/// Decode payload as `Vec<f64>` for float64 arrays.
229+
fn data_as_f64(&self) -> PyResult<Vec<f64>> {
230+
self.inner
231+
.data_as_f64()
232+
.map(|v| v.to_vec())
233+
.map_err(py_err_from)
234+
}
235+
236+
/// Decode payload as `Vec<bool>` for boolean arrays.
237+
fn data_as_bool(&self) -> PyResult<Vec<bool>> {
238+
self.inner
239+
.data_as_bool()
240+
.map(|v| v.to_vec())
241+
.map_err(py_err_from)
242+
}
243+
244+
/// Decode payload as a UTF-8 string for `DataType::String`.
245+
///
246+
/// Uses the same rules as the C FFI helper:
247+
/// - strips trailing `\0` bytes
248+
/// - errors on invalid UTF-8 or wrong type.
249+
fn data_as_string(&self) -> PyResult<String> {
250+
self.inner
251+
.data_as_string()
252+
.map_err(py_err_from)
253+
}
254+
156255
/// Human-readable header string (no payload).
157256
fn header_string(&self) -> String {
158257
self.inner.header_string()

0 commit comments

Comments
 (0)