Skip to content

Commit 3a81b30

Browse files
Add MS3Record.parse_into() instance method for reuse-based parsing
1 parent 2f040f7 commit 3a81b30

File tree

3 files changed

+87
-6
lines changed

3 files changed

+87
-6
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.6.0] - 2026-03-12
11+
12+
### Added
13+
- `MS3Record.parse_into()` instance method for reuse-based parsing in high-throughput
14+
loops to eliminating per-record allocation/deallocation overhead.
15+
16+
### Changed
17+
- `MS3Record.sourceid` property now skips an unnecessary NULL check.
18+
- `cdata_to_string()` no longer wraps `.decode()` in a redundant `str()` call.
19+
1020
## [0.5.0] - 2026-03-11
1121

1222
### Added

src/pymseed/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# Package version
2-
__version__ = "0.5.0"
2+
__version__ = "0.6.0"

src/pymseed/msrecord.py

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class MS3Record:
5151
Creating records:
5252
>>> from pymseed import MS3Record, DataEncoding
5353
>>> msr = MS3Record()
54-
>>> msr.sourceid = "FDSN:NET_STA_LOC_B_S_s"
54+
>>> msr.sourceid = "FDSN:NET_STA_LOC_B_S_X"
5555
>>> msr.set_starttime_str("2024-01-01T00:00:00Z")
5656
>>> msr.samprate = 100.0
5757
>>> msr.encoding = DataEncoding.STEIM2
@@ -117,7 +117,7 @@ def __init__(
117117
>>> # Create empty record
118118
>>> from pymseed import MS3Record
119119
>>> msr = MS3Record()
120-
>>> msr.sourceid = "FDSN:IU_COLA_00_B_H_Z"
120+
>>> msr.sourceid = "FDSN:XX_TEST__B_S_X"
121121
>>> msr.samprate = 20.0
122122
>>>
123123
>>> # Create with specific encoding for Steim2 compression
@@ -249,7 +249,7 @@ def sourceid(self) -> Optional[str]:
249249
Returns:
250250
Source identifier string, or None if not set
251251
"""
252-
return cdata_to_string(self._msr.sid)
252+
return ffi.string(self._msr.sid).decode("utf-8")
253253

254254
@sourceid.setter
255255
def sourceid(self, value: str) -> None:
@@ -1270,7 +1270,7 @@ def with_datasamples(self, data_samples: Sequence[Any], sample_type: str):
12701270
12711271
>>> from pymseed import MS3Record, DataEncoding
12721272
>>> msr = MS3Record()
1273-
>>> msr.sourceid = "FDSN:XX_TEST__L_S_X"
1273+
>>> msr.sourceid = "FDSN:XX_TEST__H_S_X"
12741274
>>> msr.reclen = 512
12751275
>>> msr.formatversion = 3
12761276
>>> msr.set_starttime_str("2023-01-02T01:02:03.123456789Z")
@@ -1820,7 +1820,7 @@ def parse(
18201820
>>> with open('examples/example_data.mseed', 'rb') as f:
18211821
... raw = f.read(512) # first record
18221822
>>> msr = MS3Record.parse(raw)
1823-
>>> msr.sourceid is not None
1823+
>>> msr.sourceid != ''
18241824
True
18251825
18261826
Parse with data samples for repacking (v2 to v3 conversion):
@@ -1859,3 +1859,74 @@ def parse(
18591859
return msr
18601860
else:
18611861
raise MiniSEEDError(status, "Error parsing miniSEED record")
1862+
1863+
def parse_into(
1864+
self,
1865+
buffer: Any,
1866+
unpack_data: bool = False,
1867+
validate_crc: bool = True,
1868+
verbose: int = 0,
1869+
) -> "MS3Record":
1870+
"""Parse a miniSEED record into this existing instance, reusing the C struct.
1871+
1872+
This is an optimized alternative to :meth:`parse` for high-throughput loops
1873+
where the same ``MS3Record`` object is reused across many records. By passing
1874+
the existing C struct pointer to ``msr3_parse``, the library can reuse the
1875+
allocation rather than free and reallocate on every call, eliminating the
1876+
``__init__``/``__del__`` overhead in tight loops.
1877+
1878+
Args:
1879+
buffer: Bytes-like object containing a single miniSEED record.
1880+
Must support the buffer protocol (e.g. ``bytes``,
1881+
``bytearray``, ``memoryview``).
1882+
unpack_data: If ``True``, decode and unpack data samples.
1883+
Default is ``False``.
1884+
validate_crc: If ``True``, validate the CRC checksum when present
1885+
(miniSEED v3 only). Default is ``True``.
1886+
verbose: Verbosity level for libmseed diagnostics. Default is 0.
1887+
1888+
Returns:
1889+
MS3Record: ``self``, updated in place with the new record's fields.
1890+
1891+
Raises:
1892+
MiniSEEDError: If the buffer does not contain a complete, valid
1893+
miniSEED record.
1894+
1895+
Example:
1896+
>>> from pymseed import MS3Record
1897+
>>> msr = MS3Record()
1898+
>>> with open('examples/example_data.mseed', 'rb') as f:
1899+
... buf = f.read(512)
1900+
>>> _ = msr.parse_into(buf)
1901+
>>> msr.samplecnt > 0
1902+
True
1903+
>>> msr.sourceid != ''
1904+
True
1905+
1906+
See Also:
1907+
parse(): Classmethod that allocates a new MS3Record per call
1908+
from_buffer(): Iterate over multiple records in a buffer
1909+
"""
1910+
buf_ptr = ffi.from_buffer(buffer)
1911+
msr_ptr = ffi.new("MS3Record **")
1912+
msr_ptr[0] = self._msr
1913+
1914+
parse_flags = 0
1915+
if unpack_data:
1916+
parse_flags |= clibmseed.MSF_UNPACKDATA
1917+
if validate_crc:
1918+
parse_flags |= clibmseed.MSF_VALIDATECRC
1919+
1920+
status = clibmseed.msr3_parse(
1921+
buf_ptr,
1922+
len(buf_ptr),
1923+
msr_ptr,
1924+
parse_flags,
1925+
verbose,
1926+
)
1927+
1928+
if status != clibmseed.MS_NOERROR:
1929+
raise MiniSEEDError(status, "Error parsing miniSEED record")
1930+
1931+
self._msr = msr_ptr[0]
1932+
return self

0 commit comments

Comments
 (0)