use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Mutex, OnceLock};
use deq_decoder_abi::host::{DecoderLibrary, LoadedDecoder};
use serde::{Deserialize, Serialize};
#[cfg(feature = "cli")]
use structdoc::StructDoc;
use crate::decoder::blackbox_decoder::{DecodingHypergraph, ParityFactor};
use crate::decoder::thread_pooling::{
DecodeError, DecodeRequest, DecoderInstance, ThreadPoolingConfig, ThreadPoolingDecoder,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "cli", derive(StructDoc))]
#[serde(deny_unknown_fields)]
pub struct DynLibDecoderConfig {
#[serde(flatten)]
pub thread_pooling_config: ThreadPoolingConfig,
pub library: PathBuf,
#[cfg_attr(feature = "cli", structdoc(skip))]
#[serde(default)]
pub decoder_config: serde_json::Value,
}
fn library_cache() -> &'static Mutex<HashMap<PathBuf, &'static DecoderLibrary>> {
static CACHE: OnceLock<Mutex<HashMap<PathBuf, &'static DecoderLibrary>>> = OnceLock::new();
CACHE.get_or_init(|| Mutex::new(HashMap::new()))
}
fn get_or_load_library(path: &Path) -> &'static DecoderLibrary {
let mut cache = library_cache().lock().unwrap();
if let Some(library) = cache.get(path) {
return library;
}
let library = unsafe { DecoderLibrary::load(path) }
.unwrap_or_else(|e| panic!("failed to load decoder plugin {}: {e}", path.display()));
cache.insert(path.to_path_buf(), library);
library
}
pub struct DynLibInstance {
loaded: LoadedDecoder,
active_edges: Vec<u64>,
}
impl DecoderInstance for DynLibInstance {
fn new(hypergraph: &DecodingHypergraph, config: &serde_json::Value) -> Self {
let config: DynLibDecoderConfig = serde_json::from_value(config.clone()).expect("invalid DynLibDecoderConfig");
let library = get_or_load_library(&config.library);
let active_edges = crate::decoder::blackbox_util::active_edge_indices(hypergraph);
let mut edge_probs = Vec::with_capacity(active_edges.len());
let mut edge_offsets = Vec::with_capacity(active_edges.len() + 1);
edge_offsets.push(0u64);
let mut edge_vertices = Vec::new();
for &edge in &active_edges {
let hyperedge = &hypergraph.hyperedges[edge as usize];
edge_probs.push(hyperedge.probability);
edge_vertices.extend_from_slice(&hyperedge.vertices);
edge_offsets.push(edge_vertices.len() as u64);
}
let decoder_config = serde_json::to_string(&config.decoder_config).expect("serialize decoder_config");
let loaded = LoadedDecoder::create(
library,
hypergraph.vertex_num,
&edge_probs,
&edge_offsets,
&edge_vertices,
&decoder_config,
)
.unwrap_or_else(|e| panic!("plugin {} failed to build decoder: {e}", config.library.display()));
Self { loaded, active_edges }
}
fn decode(&mut self, request: DecodeRequest<'_>) -> Result<ParityFactor, DecodeError> {
let mut subgraph = Vec::new();
match self
.loaded
.decode(request.syndrome.size, &request.syndrome.data, &mut subgraph)
{
Ok(()) => {
let subgraph = subgraph
.into_iter()
.map(|index| {
self.active_edges.get(index as usize).copied().ok_or_else(|| {
DecodeError::Backend(format!(
"decoder plugin returned edge {index}, but only {} edges were loaded",
self.active_edges.len()
))
})
})
.collect::<Result<_, _>>()?;
Ok(ParityFactor { subgraph })
}
Err(error) => Err(DecodeError::Backend(error.to_string())),
}
}
fn reset(&mut self) {}
}
pub type DynLibDecoder = ThreadPoolingDecoder<DynLibInstance>;