This commit is contained in:
Tsuki 2026-01-06 08:17:44 +08:00
parent c6c15c95da
commit 2ae2d579b8
16 changed files with 243 additions and 12 deletions

101
Cargo.lock generated
View File

@ -707,6 +707,15 @@ dependencies = [
"hashbrown 0.16.1", "hashbrown 0.16.1",
] ]
[[package]]
name = "indoc"
version = "2.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
dependencies = [
"rustversion",
]
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.17" version = "0.4.17"
@ -884,6 +893,15 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.8.9" version = "0.8.9"
@ -946,7 +964,7 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"cfg-if", "cfg-if",
"libc", "libc",
"memoffset", "memoffset 0.7.1",
"pin-utils", "pin-utils",
] ]
@ -1182,6 +1200,67 @@ dependencies = [
"syn 2.0.111", "syn 2.0.111",
] ]
[[package]]
name = "pyo3"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d"
dependencies = [
"indoc",
"libc",
"memoffset 0.9.1",
"once_cell",
"portable-atomic",
"pyo3-build-config",
"pyo3-ffi",
"pyo3-macros",
"unindent",
]
[[package]]
name = "pyo3-build-config"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6"
dependencies = [
"target-lexicon",
]
[[package]]
name = "pyo3-ffi"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089"
dependencies = [
"libc",
"pyo3-build-config",
]
[[package]]
name = "pyo3-macros"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
"quote",
"syn 2.0.111",
]
[[package]]
name = "pyo3-macros-backend"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9"
dependencies = [
"heck",
"proc-macro2",
"pyo3-build-config",
"quote",
"syn 2.0.111",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.42" version = "1.0.42"
@ -1270,6 +1349,14 @@ dependencies = [
"thiserror 2.0.17", "thiserror 2.0.17",
] ]
[[package]]
name = "rbufrp"
version = "0.1.0"
dependencies = [
"pyo3",
"rbufr",
]
[[package]] [[package]]
name = "rdst" name = "rdst"
version = "0.20.14" version = "0.20.14"
@ -1573,6 +1660,12 @@ dependencies = [
"windows 0.61.3", "windows 0.61.3",
] ]
[[package]]
name = "target-lexicon"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1dd07eb858a2067e2f3c7155d54e929265c264e6f37efe3ee7a8d1b5a1dd0ba"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.23.0" version = "3.23.0"
@ -1718,6 +1811,12 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unindent"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"

View File

@ -1,2 +1,2 @@
[workspace] [workspace]
members = ["rbufr", "gen"] members = ["rbufr", "gen", "rbufrp"]

View File

@ -12,6 +12,7 @@ use crate::structs::GENCENTER;
use crate::structs::versions::{BUFRMessage, MessageVersion}; use crate::structs::versions::{BUFRMessage, MessageVersion};
use crate::tables::*; use crate::tables::*;
#[derive(Clone)]
pub struct MessageBlock { pub struct MessageBlock {
message: BUFRMessage, message: BUFRMessage,
} }

View File

@ -11,7 +11,6 @@ use genlib::{
prelude::{BUFRTableB, BUFRTableBitMap, BUFRTableD}, prelude::{BUFRTableB, BUFRTableBitMap, BUFRTableD},
tables::{ArchivedBTableEntry, ArchivedDTableEntry}, tables::{ArchivedBTableEntry, ArchivedDTableEntry},
}; };
use rustc_hash::FxHashMap;
use std::{fmt::Display, ops::Deref}; use std::{fmt::Display, ops::Deref};
const MISS_VAL: f64 = 99999.999999; const MISS_VAL: f64 = 99999.999999;
@ -33,10 +32,6 @@ struct Cache<'a> {
master_d: &'a BUFRTableD, master_d: &'a BUFRTableD,
local_b: Option<&'a BUFRTableB>, local_b: Option<&'a BUFRTableB>,
local_d: Option<&'a BUFRTableD>, local_d: Option<&'a BUFRTableD>,
// Cache
b_cache: FxHashMap<FXY, &'a ArchivedBTableEntry>,
d_cache: FxHashMap<FXY, &'a ArchivedDTableEntry>,
} }
impl<'a> Cache<'a> { impl<'a> Cache<'a> {
@ -51,8 +46,6 @@ impl<'a> Cache<'a> {
master_d, master_d,
local_b, local_b,
local_d, local_d,
b_cache: FxHashMap::default(),
d_cache: FxHashMap::default(),
} }
} }

View File

@ -3,11 +3,11 @@ use nom::{
bytes::complete::{tag, take}, bytes::complete::{tag, take},
number::complete::{be_u8, be_u16, be_u24}, number::complete::{be_u8, be_u16, be_u24},
}; };
pub mod bit; pub(super) mod bit;
pub(super) mod tools; pub(super) mod tools;
pub mod versions; pub mod versions;
#[cfg(feature = "opera")] #[cfg(feature = "opera")]
pub(super) const GENCENTER: u16 = 247; pub const GENCENTER: u16 = 247;
#[inline] #[inline]
pub fn skip(n: usize) -> impl Fn(&[u8]) -> IResult<&[u8], ()> { pub fn skip(n: usize) -> impl Fn(&[u8]) -> IResult<&[u8], ()> {

View File

@ -1,7 +1,8 @@
use librbufr::{decoder::Decoder, parser::parse}; use librbufr::{decoder::Decoder, parser::parse};
#[test]
fn test_dec() { fn test_dec() {
let file = parse("/Users/xiang.li1/Downloads/36_2025-12-22T11_00_00.bufr").unwrap(); let file = parse("/Users/tsuki/Downloads/36_2025-12-17T09_00_00.bufr.nc").unwrap();
for msg in file.messages() { for msg in file.messages() {
let mut decoder = Decoder::from_message(msg).unwrap(); let mut decoder = Decoder::from_message(msg).unwrap();
let record = decoder.decode(msg).unwrap(); let record = decoder.decode(msg).unwrap();

1
rbufrp/.python-version Normal file
View File

@ -0,0 +1 @@
3.11

16
rbufrp/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "rbufrp"
version = "0.1.0"
edition = "2024"
[lib]
name = "_core"
# "cdylib" is necessary to produce a shared library for Python to import from.
crate-type = ["cdylib"]
[dependencies]
# "extension-module" tells pyo3 we want to build an extension module (skips linking against libpython.so)
# "abi3-py39" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.9
pyo3 = { version = "0.27.1", features = ["extension-module", "abi3-py38"] }
rbufr = { path = "../rbufr" }

0
rbufrp/README.md Normal file
View File

25
rbufrp/pyproject.toml Normal file
View File

@ -0,0 +1,25 @@
[project]
name = "rbufrp"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
authors = [
{ name = "Tsuki", email = "qwin7989@gmail.com" }
]
requires-python = ">=3.11"
dependencies = []
[project.scripts]
rbufrp = "rbufrp:main"
[tool.maturin]
module-name = "rbufrp._core"
python-packages = ["rbufrp"]
python-source = "src"
[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { file = "src/**/*.rs" }, { file = "Cargo.toml" }, { file = "Cargo.lock" }]
[build-system]
requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"

81
rbufrp/src/lib.rs Normal file
View File

@ -0,0 +1,81 @@
use librbufr::{Decoder, parse};
use pyo3::prelude::*;
#[pymodule]
mod _core {
use librbufr::{
block::{BUFRFile as IB, MessageBlock as IM},
decoder::BUFRData,
errors::Error,
parse,
};
use pyo3::prelude::*;
#[pyfunction]
fn hello_from_bin() -> String {
"Hello from rbufrp!".to_string()
}
#[pyclass]
struct BUFRDecoder {}
#[pymethods]
impl BUFRDecoder {
#[new]
fn new() -> Self {
BUFRDecoder {}
}
fn decode_bufr(&self, file_path: &str) -> PyResult<BUFRFile> {
let parsed = parse(file_path).map_err(|e| match e {
Error::Io(io_err) => {
PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("IO Error: {}", io_err))
}
Error::ParseError(parse_err) => PyErr::new::<pyo3::exceptions::PyValueError, _>(
format!("Parse Error: {}", parse_err),
),
Error::Nom(nom_err) => PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
"Parse Error: {}",
nom_err
)),
_ => PyErr::new::<pyo3::exceptions::PyException, _>(
"An unknown error occurred during BUFR decoding.",
),
})?;
Ok(BUFRFile(parsed))
}
}
#[pyclass]
struct BUFRFile(IB);
#[pymethods]
impl BUFRFile {
fn __repr__(&self) -> String {
format!("BUFRFile with {} messages", self.0.message_count())
}
fn message_count(&self) -> usize {
self.0.message_count()
}
fn get_message(&self, index: usize) -> PyResult<BUFRMessage> {
let message = self.0.message_at(index).ok_or_else(|| {
PyErr::new::<pyo3::exceptions::PyIndexError, _>("Message index out of range")
})?;
Ok(BUFRMessage {
message: message.clone(),
})
}
}
#[pyclass]
struct BUFRMessage {
message: IM,
}
}

View File

@ -0,0 +1,5 @@
from rbufrp._core import hello_from_bin
def main() -> None:
print(hello_from_bin())

Binary file not shown.

BIN
rbufrp/src/rbufrp/_core.abi3.so Executable file

Binary file not shown.

View File

@ -0,0 +1 @@
def hello_from_bin() -> str: ...

8
rbufrp/uv.lock generated Normal file
View File

@ -0,0 +1,8 @@
version = 1
revision = 3
requires-python = ">=3.11"
[[package]]
name = "rbufrp"
version = "0.1.0"
source = { editable = "." }