use alloc::borrow::Cow;
use alloc::string::{String, ToString};
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt;
use core::ops::BitOr;
use core::sync::atomic::{AtomicU32, Ordering};
use thiserror::Error;
use crate::DataType;
use crate::compressor::core as engine;
use crate::compressor::core::data::{Bytes, Stream, type_mask};
use crate::compressor::core::error::ExecError;
use crate::compressor::core::exec::{Edge as CoreEdge, GraphCx, SegmenterCx, SelectorCx};
use crate::compressor::core::params::{LocalParams as CoreParams, RefParam};
use crate::compressor::core::registry::dict::LoadError;
use crate::compressor::core::registry::{
self as reg, Callback, GraphDef, GraphKind, NodeDef, Registry, RuntimeParams,
};
mod config;
pub mod params;
pub use config::{ClusteringConfig, ConfigError, GbtModel, GbtNode, MlSelectorConfig};
pub const CLUSTERING_TAG_METADATA_ID: i32 = params::clustering::TAG_METADATA_ID;
const CLUSTERING_CONFIG_PARAM: i32 = engine::graphs::clustering::CONFIG_PARAM_ID;
use crate::dict::{DictBundle, DictError, DictId};
#[derive(Error, Debug, Clone, Copy, Eq, PartialEq)]
#[error("{0} is not the ID of a supported standard graph or node")]
pub struct UnknownId(pub u32);
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[non_exhaustive]
#[repr(u32)]
pub enum StandardNode {
DeltaInt = 2,
TransposeSplit = 3,
Zigzag = 4,
DispatchNByTag = 5,
Float32Deconstruct = 6,
Bfloat16Deconstruct = 7,
Float16Deconstruct = 8,
FieldLz = 9,
ConvertStructToSerial = 10,
ConvertNumToStructLe = 11,
ConvertNumToSerialLe = 12,
ConvertSerialToStruct = 13,
ConvertSerialToStruct2 = 14,
ConvertSerialToStruct4 = 15,
ConvertSerialToStruct8 = 16,
ConvertStructToNumLe = 17,
ConvertStructToNumBe = 18,
ConvertSerialToNum8 = 19,
ConvertSerialToNumLe16 = 20,
ConvertSerialToNumLe32 = 21,
ConvertSerialToNumLe64 = 22,
ConvertSerialToNumBe16 = 23,
ConvertSerialToNumBe32 = 24,
ConvertSerialToNumBe64 = 25,
SeparateStringComponents = 26,
Bitunpack = 27,
RangePack = 28,
MergeSorted = 29,
Prefix = 30,
DivideBy = 31,
DispatchString = 32,
ConcatSerial = 33,
ConcatNum = 34,
ConcatStruct = 35,
ConcatString = 36,
DedupNum = 37,
ParseInt = 38,
InterleaveString = 39,
TokenizeStruct = 40,
TokenizeNumeric = 41,
TokenizeString = 42,
QuantizeOffsets = 43,
QuantizeLengths = 44,
BitsplitTop8 = 45,
BitsplitFp = 46,
BitsplitBf16 = 47,
Partition = 48,
SplitByrange = 49,
SentinelByte = 50,
SentinelNum = 51,
Lz = 52,
MuxLengths = 53,
SparseNum = 54,
SparseNumAuto = 55,
SetStringLens = 57,
FseV2 = 58,
HuffmanV2 = 59,
HuffmanStructV2 = 60,
FseNcount = 61,
Zstd = 62,
BitpackSerial = 63,
BitpackInt = 64,
Flatpack = 65,
SplitN = 66,
SplitNStruct = 67,
SplitNNum = 68,
SplitByStruct = 69,
ConstantSerial = 70,
ConstantFixed = 71,
TokenizeSorted = 72,
TokenizeStringSorted = 73,
DedupNumTrusted = 74,
BitSplit = 75,
Lz4 = 86,
PivcoHuffman = 87,
}
impl TryFrom<u32> for StandardNode {
type Error = UnknownId;
fn try_from(id: u32) -> Result<Self, Self::Error> {
Ok(match id {
2 => Self::DeltaInt,
3 => Self::TransposeSplit,
4 => Self::Zigzag,
5 => Self::DispatchNByTag,
6 => Self::Float32Deconstruct,
7 => Self::Bfloat16Deconstruct,
8 => Self::Float16Deconstruct,
9 => Self::FieldLz,
10 => Self::ConvertStructToSerial,
11 => Self::ConvertNumToStructLe,
12 => Self::ConvertNumToSerialLe,
13 => Self::ConvertSerialToStruct,
14 => Self::ConvertSerialToStruct2,
15 => Self::ConvertSerialToStruct4,
16 => Self::ConvertSerialToStruct8,
17 => Self::ConvertStructToNumLe,
18 => Self::ConvertStructToNumBe,
19 => Self::ConvertSerialToNum8,
20 => Self::ConvertSerialToNumLe16,
21 => Self::ConvertSerialToNumLe32,
22 => Self::ConvertSerialToNumLe64,
23 => Self::ConvertSerialToNumBe16,
24 => Self::ConvertSerialToNumBe32,
25 => Self::ConvertSerialToNumBe64,
26 => Self::SeparateStringComponents,
27 => Self::Bitunpack,
28 => Self::RangePack,
29 => Self::MergeSorted,
30 => Self::Prefix,
31 => Self::DivideBy,
32 => Self::DispatchString,
33 => Self::ConcatSerial,
34 => Self::ConcatNum,
35 => Self::ConcatStruct,
36 => Self::ConcatString,
37 => Self::DedupNum,
38 => Self::ParseInt,
39 => Self::InterleaveString,
40 => Self::TokenizeStruct,
41 => Self::TokenizeNumeric,
42 => Self::TokenizeString,
43 => Self::QuantizeOffsets,
44 => Self::QuantizeLengths,
45 => Self::BitsplitTop8,
46 => Self::BitsplitFp,
47 => Self::BitsplitBf16,
48 => Self::Partition,
49 => Self::SplitByrange,
50 => Self::SentinelByte,
51 => Self::SentinelNum,
52 => Self::Lz,
53 => Self::MuxLengths,
54 => Self::SparseNum,
55 => Self::SparseNumAuto,
57 => Self::SetStringLens,
58 => Self::FseV2,
59 => Self::HuffmanV2,
60 => Self::HuffmanStructV2,
61 => Self::FseNcount,
62 => Self::Zstd,
63 => Self::BitpackSerial,
64 => Self::BitpackInt,
65 => Self::Flatpack,
66 => Self::SplitN,
67 => Self::SplitNStruct,
68 => Self::SplitNNum,
69 => Self::SplitByStruct,
70 => Self::ConstantSerial,
71 => Self::ConstantFixed,
72 => Self::TokenizeSorted,
73 => Self::TokenizeStringSorted,
74 => Self::DedupNumTrusted,
75 => Self::BitSplit,
86 => Self::Lz4,
87 => Self::PivcoHuffman,
_ => return Err(UnknownId(id)),
})
}
}
impl From<StandardNode> for u32 {
fn from(value: StandardNode) -> Self {
value as Self
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[non_exhaustive]
#[repr(u32)]
pub enum StandardGraph {
Store = 2,
Fse = 3,
Huffman = 4,
HuffmanHuf0 = 5,
HuffmanPivco = 6,
Entropy = 7,
Constant = 8,
Zstd = 9,
Bitpack = 10,
Flatpack = 11,
FieldLz = 12,
CompressGeneric = 13,
SelectGenericLzBackend = 14,
SegmentNumeric = 15,
SelectNumeric = 16,
MlSelector = 17,
Clustering = 18,
TryParseInt = 19,
SimpleDataDescriptionLanguage = 20,
SimpleDataDescriptionLanguageV2 = 21,
Lz4 = 22,
PartitionBitpack = 23,
SegmentNum8FromSerial = 24,
SegmentNum16FromSerial = 25,
SegmentNum32FromSerial = 26,
SegmentNum64FromSerial = 27,
Lz = 28,
SegmentSerial = 29,
TransformerNumeric = 30,
BruteForce = 31,
SerialStore = 1,
Store1 = 32,
StringStore = 33,
Compress1 = 34,
SerialCompress = 35,
StructCompress = 36,
NumericCompress = 37,
StringCompress = 38,
StringSeparateCompress = 39,
BitpackSerial = 40,
BitpackInt = 41,
ConstantSerial = 42,
ConstantFixed = 43,
FseNcount = 44,
FieldLzLiterals = 45,
FieldLzLiteralsChannel = 46,
DeltaHuffman = 47,
DeltaFlatpack = 48,
DeltaZstd = 49,
DeltaHuffmanInternal = 50,
DeltaFlatpackInternal = 51,
DeltaZstdInternal = 52,
DeltaFieldLz = 53,
RangePack = 54,
RangePackZstd = 55,
TokenizeDeltaFieldLz = 56,
SplitSerial = 57,
SplitStruct = 58,
SplitNumeric = 59,
SplitString = 60,
Sddl2Chunk = 61,
NToN = 62,
MergeSorted = 63,
TransposeSplit = 64,
InterpretNum8Compress = 65,
InterpretNum16Compress = 66,
InterpretNum32Compress = 67,
InterpretNum64Compress = 68,
CompressSmallLengths = 69,
TransformerDeltaInt = 70,
TransformerRangePack = 71,
TransformerDivideByGcd = 72,
TransformerTokenizeNumeric = 73,
TransformerTokenizeNumericSorted = 74,
TransformerSparseNum = 75,
TransformerStaticCore = 76,
TransformerStaticDelta = 77,
TransformerStaticRangePack = 78,
TransformerStaticRangePackDelta = 79,
TransformerStaticZigzag = 80,
TransformerStaticGcd = 81,
TransformerStaticIndex = 82,
TransformerStaticTokSorted = 83,
TransformerStaticDeltaTok = 84,
TransformerStaticTokMono = 85,
TransformerStaticDeltaTokMono = 86,
TransformerStaticTokMonoLz = 87,
TransformerStaticDeltaTokMonoLz = 88,
TransformerStaticFallback = 89,
TransformerNumeric1 = 90,
}
impl TryFrom<u32> for StandardGraph {
type Error = UnknownId;
fn try_from(id: u32) -> Result<Self, Self::Error> {
Ok(match id {
2 => Self::Store,
3 => Self::Fse,
4 => Self::Huffman,
5 => Self::HuffmanHuf0,
6 => Self::HuffmanPivco,
7 => Self::Entropy,
8 => Self::Constant,
9 => Self::Zstd,
10 => Self::Bitpack,
11 => Self::Flatpack,
12 => Self::FieldLz,
13 => Self::CompressGeneric,
14 => Self::SelectGenericLzBackend,
15 => Self::SegmentNumeric,
16 => Self::SelectNumeric,
17 => Self::MlSelector,
18 => Self::Clustering,
19 => Self::TryParseInt,
20 => Self::SimpleDataDescriptionLanguage,
21 => Self::SimpleDataDescriptionLanguageV2,
22 => Self::Lz4,
23 => Self::PartitionBitpack,
24 => Self::SegmentNum8FromSerial,
25 => Self::SegmentNum16FromSerial,
26 => Self::SegmentNum32FromSerial,
27 => Self::SegmentNum64FromSerial,
28 => Self::Lz,
29 => Self::SegmentSerial,
30 => Self::TransformerNumeric,
31 => Self::BruteForce,
1 => Self::SerialStore,
32 => Self::Store1,
33 => Self::StringStore,
34 => Self::Compress1,
35 => Self::SerialCompress,
36 => Self::StructCompress,
37 => Self::NumericCompress,
38 => Self::StringCompress,
39 => Self::StringSeparateCompress,
40 => Self::BitpackSerial,
41 => Self::BitpackInt,
42 => Self::ConstantSerial,
43 => Self::ConstantFixed,
44 => Self::FseNcount,
45 => Self::FieldLzLiterals,
46 => Self::FieldLzLiteralsChannel,
47 => Self::DeltaHuffman,
48 => Self::DeltaFlatpack,
49 => Self::DeltaZstd,
50 => Self::DeltaHuffmanInternal,
51 => Self::DeltaFlatpackInternal,
52 => Self::DeltaZstdInternal,
53 => Self::DeltaFieldLz,
54 => Self::RangePack,
55 => Self::RangePackZstd,
56 => Self::TokenizeDeltaFieldLz,
57 => Self::SplitSerial,
58 => Self::SplitStruct,
59 => Self::SplitNumeric,
60 => Self::SplitString,
61 => Self::Sddl2Chunk,
62 => Self::NToN,
63 => Self::MergeSorted,
64 => Self::TransposeSplit,
65 => Self::InterpretNum8Compress,
66 => Self::InterpretNum16Compress,
67 => Self::InterpretNum32Compress,
68 => Self::InterpretNum64Compress,
69 => Self::CompressSmallLengths,
70 => Self::TransformerDeltaInt,
71 => Self::TransformerRangePack,
72 => Self::TransformerDivideByGcd,
73 => Self::TransformerTokenizeNumeric,
74 => Self::TransformerTokenizeNumericSorted,
75 => Self::TransformerSparseNum,
76 => Self::TransformerStaticCore,
77 => Self::TransformerStaticDelta,
78 => Self::TransformerStaticRangePack,
79 => Self::TransformerStaticRangePackDelta,
80 => Self::TransformerStaticZigzag,
81 => Self::TransformerStaticGcd,
82 => Self::TransformerStaticIndex,
83 => Self::TransformerStaticTokSorted,
84 => Self::TransformerStaticDeltaTok,
85 => Self::TransformerStaticTokMono,
86 => Self::TransformerStaticDeltaTokMono,
87 => Self::TransformerStaticTokMonoLz,
88 => Self::TransformerStaticDeltaTokMonoLz,
89 => Self::TransformerStaticFallback,
90 => Self::TransformerNumeric1,
_ => return Err(UnknownId(id)),
})
}
}
impl From<StandardGraph> for u32 {
fn from(value: StandardGraph) -> Self {
value as Self
}
}
macro_rules! id_conversions {
($public:ident, $standard:ident, $core:ident) => {
impl From<$standard> for $public {
fn from(value: $standard) -> Self {
Self(reg::$core::std(u32::from(value)))
}
}
};
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GraphId(reg::GraphId);
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct NodeId(reg::NodeId);
id_conversions!(GraphId, StandardGraph, GraphId);
id_conversions!(NodeId, StandardNode, NodeId);
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct TypeSet(u8);
impl TypeSet {
pub const ANY: Self = Self(15);
#[must_use]
pub const fn contains(self, ty: DataType) -> bool {
self.0 & type_mask(ty) != 0
}
}
impl From<DataType> for TypeSet {
fn from(ty: DataType) -> Self {
Self(type_mask(ty))
}
}
impl BitOr for TypeSet {
type Output = Self;
fn bitor(self, other: Self) -> Self {
Self(self.0 | other.0)
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct LocalParams(CoreParams);
impl LocalParams {
#[must_use]
pub fn with_int(mut self, id: i32, value: i32) -> Self {
self.0.ints.to_mut().push((id, value));
self
}
#[must_use]
pub fn with_bytes(mut self, id: i32, bytes: impl Into<Arc<[u8]>>) -> Self {
self.0.copies.push((id, bytes.into()));
self
}
#[must_use]
pub fn with_string_lengths(mut self, lengths: Vec<u32>) -> Self {
let object: Arc<dyn core::any::Any + Send + Sync> = Arc::new(lengths);
self.0.refs.push((
engine::codecs::conversion::SET_STRING_LENS_ARRAY,
RefParam(object),
));
self
}
#[must_use]
pub fn int(&self, id: i32) -> Option<i32> {
self.0.int(id)
}
}
#[derive(Clone, Debug, Default)]
pub struct NodeParams {
params: Option<LocalParams>,
dictionary: Option<DictId>,
}
impl NodeParams {
#[must_use]
pub fn with_params(mut self, params: LocalParams) -> Self {
self.params = Some(params);
self
}
#[must_use]
pub fn with_dictionary(mut self, id: DictId) -> Self {
self.dictionary = Some(id);
self
}
}
#[derive(Clone, Debug, Default)]
pub struct GraphParams {
params: Option<LocalParams>,
custom_graphs: Option<Vec<GraphId>>,
custom_nodes: Option<Vec<NodeId>>,
name: Option<String>,
}
impl GraphParams {
#[must_use]
pub fn with_params(mut self, params: LocalParams) -> Self {
self.params = Some(params);
self
}
#[must_use]
pub fn with_graphs(mut self, graphs: impl Into<Vec<GraphId>>) -> Self {
self.custom_graphs = Some(graphs.into());
self
}
#[must_use]
pub fn with_nodes(mut self, nodes: impl Into<Vec<NodeId>>) -> Self {
self.custom_nodes = Some(nodes.into());
self
}
#[must_use]
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
#[derive(Error, Debug, Clone, Eq, PartialEq)]
#[non_exhaustive]
pub enum GraphError {
#[error("unknown or foreign {0} ID")]
UnknownId(&'static str),
#[error("{0}")]
Invalid(&'static str),
#[error("graph execution failed: {0}")]
Execution(String),
#[error("{0}")]
User(&'static str),
#[error("dictionary bundle: {0}")]
Dictionary(#[from] DictError),
#[error("a dictionary bundle is already loaded")]
BundleAlreadyLoaded,
#[error("dictionary {0} is not in the loaded bundle")]
DictionaryNotFound(DictId),
}
impl From<ExecError> for GraphError {
fn from(error: ExecError) -> Self {
match error {
ExecError::User(message) => Self::User(message),
other => Self::Execution(other.to_string()),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Comment(Arc<[u8]>);
#[derive(Error, Debug, Clone, Copy, Eq, PartialEq)]
#[error("a header comment holds 1 to 10000 bytes, not {0}")]
pub struct InvalidComment(pub usize);
impl TryFrom<&[u8]> for Comment {
type Error = InvalidComment;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
if bytes.is_empty() || bytes.len() > engine::layout::COMMENT_MAX {
return Err(InvalidComment(bytes.len()));
}
Ok(Self(Arc::from(bytes)))
}
}
impl AsRef<[u8]> for Comment {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl Comment {
pub(crate) fn shared(&self) -> Arc<[u8]> {
Arc::clone(&self.0)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct InputView<'v> {
data_type: DataType,
width: usize,
bytes: &'v [u8],
lens: &'v [u32],
metadata: &'v [(i32, i32)],
}
impl<'v> InputView<'v> {
fn new(stream: &'v Stream<'_>) -> Self {
Self {
data_type: stream.ty(),
width: stream.width(),
bytes: stream.bytes(),
lens: stream.lens(),
metadata: stream.metadata(),
}
}
fn to_stream(self) -> Stream<'v> {
let mut stream = match self.data_type {
DataType::String => Stream::strings(self.bytes, self.lens),
ty => Stream::new(ty, self.width, Bytes::Borrowed(self.bytes)),
};
for &(id, value) in self.metadata {
stream.set_metadata(id, value);
}
stream
}
#[must_use]
pub fn int_metadata(&self, id: i32) -> Option<i32> {
self.metadata
.iter()
.find(|&&(key, _)| key == id)
.map(|&(_, value)| value)
}
#[must_use]
pub const fn data_type(&self) -> DataType {
self.data_type
}
#[must_use]
pub const fn width(&self) -> usize {
self.width
}
#[must_use]
pub const fn bytes(&self) -> &'v [u8] {
self.bytes
}
#[must_use]
pub const fn string_lengths(&self) -> &'v [u32] {
self.lens
}
#[must_use]
pub const fn len(&self) -> usize {
match self.data_type {
DataType::String => self.lens.len(),
_ => self.bytes.len() / self.width,
}
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Edge(CoreEdge);
pub trait Selector: Send + Sync + 'static {
fn select(
&self,
cx: &mut SelectorContext<'_>,
input: InputView<'_>,
successors: &[GraphId],
) -> GraphId;
}
pub trait FunctionGraph: Send + Sync + 'static {
fn compress(&self, cx: &mut GraphContext<'_>, inputs: &[Edge]) -> Result<(), GraphError>;
}
pub trait Segmenter: Send + Sync + 'static {
fn segment(&self, cx: &mut SegmenterContext<'_>) -> Result<(), GraphError>;
}
trait SelectorApi {
fn level(&self) -> i32;
fn format_version(&self) -> u32;
fn depth(&self) -> u32;
fn node_supported(&self, node: reg::NodeId) -> bool;
fn local_int(&self, id: i32) -> Option<i32>;
fn set_params(&mut self, params: CoreParams);
fn try_graph(&mut self, input: &InputView<'_>, graph: reg::GraphId)
-> Result<usize, ExecError>;
}
impl SelectorApi for SelectorCx<'_, '_, '_> {
fn level(&self) -> i32 {
SelectorCx::level(self)
}
fn format_version(&self) -> u32 {
SelectorCx::format_version(self)
}
fn depth(&self) -> u32 {
SelectorCx::depth(self)
}
fn node_supported(&self, node: reg::NodeId) -> bool {
self.is_node_supported(node)
}
fn local_int(&self, id: i32) -> Option<i32> {
self.params.int(id)
}
fn set_params(&mut self, params: CoreParams) {
self.set_successor_params(params);
}
fn try_graph(
&mut self,
input: &InputView<'_>,
graph: reg::GraphId,
) -> Result<usize, ExecError> {
SelectorCx::try_graph(self, &input.to_stream(), graph)
}
}
pub struct SelectorContext<'c> {
api: &'c mut dyn SelectorApi,
}
impl fmt::Debug for SelectorContext<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("SelectorContext")
}
}
impl SelectorContext<'_> {
#[must_use]
pub fn level(&self) -> i32 {
self.api.level()
}
#[must_use]
pub fn format_version(&self) -> u32 {
self.api.format_version()
}
#[must_use]
pub fn depth(&self) -> u32 {
self.api.depth()
}
#[must_use]
pub fn is_node_supported(&self, node: impl Into<NodeId>) -> bool {
self.api.node_supported(node.into().0)
}
#[must_use]
pub fn local_int(&self, id: i32) -> Option<i32> {
self.api.local_int(id)
}
pub fn set_successor_params(&mut self, params: LocalParams) {
self.api.set_params(params.0);
}
pub fn try_graph(
&mut self,
input: &InputView<'_>,
graph: impl Into<GraphId>,
) -> Result<usize, GraphError> {
Ok(self.api.try_graph(input, graph.into().0)?)
}
}
trait GraphApi {
fn view(&self, edge: CoreEdge) -> Result<InputView<'_>, ExecError>;
fn run_node(
&mut self,
node: reg::NodeId,
inputs: &[CoreEdge],
params: Option<&CoreParams>,
) -> Result<Vec<CoreEdge>, ExecError>;
fn set_destination(
&mut self,
edges: &[CoreEdge],
graph: reg::GraphId,
params: Option<RuntimeParams>,
) -> Result<(), ExecError>;
fn set_metadata(&mut self, edge: CoreEdge, id: i32, value: i32) -> Result<(), ExecError>;
fn custom_graphs(&self) -> Vec<reg::GraphId>;
fn custom_nodes(&self) -> Vec<reg::NodeId>;
fn local_int(&self, id: i32) -> Option<i32>;
fn level(&self) -> i32;
fn format_version(&self) -> u32;
fn depth(&self) -> u32;
fn node_supported(&self, node: reg::NodeId) -> bool;
fn try_graph(&mut self, input: CoreEdge, graph: reg::GraphId) -> Result<usize, ExecError>;
fn run_split(&mut self, edge: CoreEdge, sizes: &[usize]) -> Result<Vec<CoreEdge>, ExecError>;
fn run_dispatch(
&mut self,
edge: CoreEdge,
instructions: &DispatchInstructions,
) -> Result<Vec<CoreEdge>, ExecError>;
fn run_dispatch_string(
&mut self,
edge: CoreEdge,
nb_outputs: i32,
indices: &[u16],
) -> Result<Vec<CoreEdge>, ExecError>;
}
impl GraphApi for GraphCx<'_, '_, '_> {
fn view(&self, edge: CoreEdge) -> Result<InputView<'_>, ExecError> {
Ok(InputView::new(self.data(edge)?))
}
fn run_node(
&mut self,
node: reg::NodeId,
inputs: &[CoreEdge],
params: Option<&CoreParams>,
) -> Result<Vec<CoreEdge>, ExecError> {
GraphCx::run_node(self, node, inputs, params)
}
fn set_destination(
&mut self,
edges: &[CoreEdge],
graph: reg::GraphId,
params: Option<RuntimeParams>,
) -> Result<(), ExecError> {
GraphCx::set_destination(self, edges, graph, params)
}
fn set_metadata(&mut self, edge: CoreEdge, id: i32, value: i32) -> Result<(), ExecError> {
GraphCx::set_metadata(self, edge, id, value)
}
fn custom_graphs(&self) -> Vec<reg::GraphId> {
self.eff.custom_graphs.to_vec()
}
fn custom_nodes(&self) -> Vec<reg::NodeId> {
self.eff.custom_nodes.to_vec()
}
fn local_int(&self, id: i32) -> Option<i32> {
self.eff.params.int(id)
}
fn level(&self) -> i32 {
self.ex.p.level
}
fn format_version(&self) -> u32 {
self.ex.p.version
}
fn depth(&self) -> u32 {
self.depth
}
fn node_supported(&self, node: reg::NodeId) -> bool {
self.is_node_supported(node)
}
fn try_graph(&mut self, input: CoreEdge, graph: reg::GraphId) -> Result<usize, ExecError> {
let stream = self.data(input)?.clone();
self.ex
.try_graph(core::slice::from_ref(&stream), graph, None)
}
fn run_split(&mut self, edge: CoreEdge, sizes: &[usize]) -> Result<Vec<CoreEdge>, ExecError> {
engine::graphs::run_split_node(self, edge, sizes)
}
fn run_dispatch(
&mut self,
edge: CoreEdge,
instructions: &DispatchInstructions,
) -> Result<Vec<CoreEdge>, ExecError> {
engine::graphs::run_dispatch_node(self, edge, &instructions.clone().into())
}
fn run_dispatch_string(
&mut self,
edge: CoreEdge,
nb_outputs: i32,
indices: &[u16],
) -> Result<Vec<CoreEdge>, ExecError> {
engine::graphs::run_dispatch_string_node(self, edge, nb_outputs, indices)
}
}
pub struct GraphContext<'c> {
api: &'c mut dyn GraphApi,
}
impl fmt::Debug for GraphContext<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("GraphContext")
}
}
fn core_edges(edges: &[Edge]) -> Vec<CoreEdge> {
edges.iter().map(|edge| edge.0).collect()
}
impl GraphContext<'_> {
pub fn data(&self, edge: Edge) -> Result<InputView<'_>, GraphError> {
Ok(self.api.view(edge.0)?)
}
pub fn set_int_metadata(&mut self, edge: Edge, id: i32, value: i32) -> Result<(), GraphError> {
Ok(self.api.set_metadata(edge.0, id, value)?)
}
pub fn run_node(
&mut self,
node: impl Into<NodeId>,
inputs: &[Edge],
) -> Result<Vec<Edge>, GraphError> {
let edges = self
.api
.run_node(node.into().0, &core_edges(inputs), None)?;
Ok(edges.into_iter().map(Edge).collect())
}
pub fn run_node_with(
&mut self,
node: impl Into<NodeId>,
inputs: &[Edge],
params: &LocalParams,
) -> Result<Vec<Edge>, GraphError> {
let edges = self
.api
.run_node(node.into().0, &core_edges(inputs), Some(¶ms.0))?;
Ok(edges.into_iter().map(Edge).collect())
}
pub fn set_destination(
&mut self,
edges: &[Edge],
graph: impl Into<GraphId>,
) -> Result<(), GraphError> {
Ok(self
.api
.set_destination(&core_edges(edges), graph.into().0, None)?)
}
pub fn set_destination_with(
&mut self,
edges: &[Edge],
graph: impl Into<GraphId>,
overrides: &GraphParams,
) -> Result<(), GraphError> {
let runtime = RuntimeParams {
params: overrides.params.as_ref().map(|params| params.0.clone()),
custom_graphs: overrides
.custom_graphs
.as_ref()
.map(|graphs| graphs.iter().map(|graph| graph.0).collect()),
custom_nodes: overrides
.custom_nodes
.as_ref()
.map(|nodes| nodes.iter().map(|node| node.0).collect()),
};
Ok(self
.api
.set_destination(&core_edges(edges), graph.into().0, Some(runtime))?)
}
#[must_use]
pub fn custom_graphs(&self) -> Vec<GraphId> {
self.api.custom_graphs().into_iter().map(GraphId).collect()
}
#[must_use]
pub fn custom_nodes(&self) -> Vec<NodeId> {
self.api.custom_nodes().into_iter().map(NodeId).collect()
}
#[must_use]
pub fn local_int(&self, id: i32) -> Option<i32> {
self.api.local_int(id)
}
#[must_use]
pub fn level(&self) -> i32 {
self.api.level()
}
#[must_use]
pub fn format_version(&self) -> u32 {
self.api.format_version()
}
#[must_use]
pub fn depth(&self) -> u32 {
self.api.depth()
}
#[must_use]
pub fn is_node_supported(&self, node: impl Into<NodeId>) -> bool {
self.api.node_supported(node.into().0)
}
pub fn try_graph(
&mut self,
input: Edge,
graph: impl Into<GraphId>,
) -> Result<usize, GraphError> {
Ok(self.api.try_graph(input.0, graph.into().0)?)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct DispatchInstructions {
pub segment_sizes: Vec<usize>,
pub tags: Vec<u32>,
pub tag_count: usize,
}
impl From<DispatchInstructions> for engine::codecs::structure::DispatchInstructions {
fn from(value: DispatchInstructions) -> Self {
Self {
segment_sizes: value.segment_sizes,
tags: value.tags,
nb_tags: value.tag_count,
}
}
}
impl GraphContext<'_> {
pub fn run_split_node(
&mut self,
edge: Edge,
segment_sizes: &[usize],
) -> Result<Vec<Edge>, GraphError> {
let edges = self.api.run_split(edge.0, segment_sizes)?;
Ok(edges.into_iter().map(Edge).collect())
}
pub fn run_dispatch_node(
&mut self,
edge: Edge,
instructions: &DispatchInstructions,
) -> Result<Vec<Edge>, GraphError> {
let edges = self.api.run_dispatch(edge.0, instructions)?;
Ok(edges.into_iter().map(Edge).collect())
}
pub fn run_dispatch_string_node(
&mut self,
edge: Edge,
output_count: usize,
indices: &[u16],
) -> Result<Vec<Edge>, GraphError> {
let count = i32::try_from(output_count).unwrap_or(i32::MAX);
let edges = self.api.run_dispatch_string(edge.0, count, indices)?;
Ok(edges.into_iter().map(Edge).collect())
}
}
trait SegmenterApi {
fn n_inputs(&self) -> usize;
fn remaining(&self) -> Vec<usize>;
fn custom_graphs(&self) -> Vec<reg::GraphId>;
fn local_int(&self, id: i32) -> Option<i32>;
fn format_version(&self) -> u32;
fn process_chunk(&mut self, n_elts: &[usize], graph: reg::GraphId) -> Result<(), ExecError>;
}
impl SegmenterApi for SegmenterCx<'_, '_, '_> {
fn n_inputs(&self) -> usize {
SegmenterCx::n_inputs(self)
}
fn remaining(&self) -> Vec<usize> {
SegmenterCx::remaining(self)
}
fn custom_graphs(&self) -> Vec<reg::GraphId> {
self.custom_graphs.to_vec()
}
fn local_int(&self, id: i32) -> Option<i32> {
self.params.int(id)
}
fn format_version(&self) -> u32 {
SegmenterCx::format_version(self)
}
fn process_chunk(&mut self, n_elts: &[usize], graph: reg::GraphId) -> Result<(), ExecError> {
SegmenterCx::process_chunk(self, n_elts, graph, None)
}
}
pub struct SegmenterContext<'c> {
api: &'c mut dyn SegmenterApi,
}
impl fmt::Debug for SegmenterContext<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("SegmenterContext")
}
}
impl SegmenterContext<'_> {
#[must_use]
pub fn n_inputs(&self) -> usize {
self.api.n_inputs()
}
#[must_use]
pub fn remaining(&self) -> Vec<usize> {
self.api.remaining()
}
#[must_use]
pub fn custom_graphs(&self) -> Vec<GraphId> {
self.api.custom_graphs().into_iter().map(GraphId).collect()
}
#[must_use]
pub fn local_int(&self, id: i32) -> Option<i32> {
self.api.local_int(id)
}
#[must_use]
pub fn format_version(&self) -> u32 {
self.api.format_version()
}
pub fn process_chunk(
&mut self,
n_elts: &[usize],
graph: impl Into<GraphId>,
) -> Result<(), GraphError> {
Ok(self.api.process_chunk(n_elts, graph.into().0)?)
}
}
struct UserSelector<S>(S);
impl<S: Selector> reg::SelectFn for UserSelector<S> {
fn select(
&self,
cx: &mut SelectorCx<'_, '_, '_>,
input: &Stream<'_>,
successors: &[reg::GraphId],
) -> reg::GraphId {
let view = InputView::new(input);
let successors: Vec<GraphId> = successors.iter().map(|&graph| GraphId(graph)).collect();
let mut context = SelectorContext { api: cx };
self.0.select(&mut context, view, &successors).0
}
}
struct UserGraph<F>(F);
impl<F: FunctionGraph> reg::GraphFn for UserGraph<F> {
fn run(&self, cx: &mut GraphCx<'_, '_, '_>, inputs: &[CoreEdge]) -> Result<(), ExecError> {
let edges: Vec<Edge> = inputs.iter().map(|&edge| Edge(edge)).collect();
let mut context = GraphContext { api: cx };
self.0
.compress(&mut context, &edges)
.map_err(|error| match error {
GraphError::User(message) => ExecError::User(message),
GraphError::Invalid(message) => ExecError::Parameter(message),
_ => ExecError::User("a function graph failed"),
})
}
}
struct UserSegmenter<S>(S);
impl<S: Segmenter> reg::SegmentFn for UserSegmenter<S> {
fn segment(&self, cx: &mut SegmenterCx<'_, '_, '_>) -> Result<(), ExecError> {
let mut context = SegmenterContext { api: cx };
self.0.segment(&mut context).map_err(|error| match error {
GraphError::User(message) => ExecError::User(message),
_ => ExecError::User("a segmenter failed"),
})
}
}
static NEXT_OWNER: AtomicU32 = AtomicU32::new(1);
#[derive(Debug)]
pub struct GraphBuilder {
registry: Registry,
}
impl Default for GraphBuilder {
fn default() -> Self {
Self::new()
}
}
impl GraphBuilder {
#[must_use]
pub fn new() -> Self {
let owner = NEXT_OWNER.fetch_add(1, Ordering::Relaxed).max(1);
Self {
registry: Registry::new(owner, reg::gid::COMPRESS_GENERIC),
}
}
fn graph_id(&self, id: GraphId) -> Result<reg::GraphId, GraphError> {
self.registry
.graph(id.0)
.map(|_| id.0)
.map_err(|_| GraphError::UnknownId("graph"))
}
fn node_id(&self, id: NodeId) -> Result<reg::NodeId, GraphError> {
self.registry
.node(id.0)
.map(|_| id.0)
.map_err(|_| GraphError::UnknownId("node"))
}
fn masks(inputs: &[TypeSet]) -> Result<Cow<'static, [u8]>, GraphError> {
if inputs.is_empty() {
return Err(GraphError::Invalid("a graph needs at least one input"));
}
Ok(Cow::Owned(inputs.iter().map(|set| set.0).collect()))
}
fn add(&mut self, def: GraphDef) -> GraphId {
GraphId(self.registry.add_graph(def))
}
pub fn static_graph(
&mut self,
head: impl Into<NodeId>,
successors: &[GraphId],
) -> Result<GraphId, GraphError> {
let head = self.node_id(head.into())?;
let spec = self
.registry
.node(head)
.map_err(|_| GraphError::UnknownId("node"))?
.spec;
let outcomes = spec.singletons.len() + spec.variable.len();
let expected_ok = if spec.variable.is_empty() {
successors.len() == outcomes
} else {
successors.len() >= spec.singletons.len() && successors.len() == outcomes
};
if !expected_ok {
return Err(GraphError::Invalid(
"the successors do not match the node's outputs",
));
}
let successors = successors
.iter()
.map(|&graph| self.graph_id(graph))
.collect::<Result<Vec<_>, _>>()?;
let inputs: Vec<u8> = spec.inputs.iter().map(|&ty| type_mask(ty)).collect();
Ok(self.add(GraphDef {
name: Cow::Owned(alloc::format!("static({})", spec.name)),
kind: GraphKind::Static,
inputs: Cow::Owned(inputs),
last_variable: spec.variable_input,
custom_graphs: Cow::Owned(successors),
custom_nodes: Cow::Owned(alloc::vec![head]),
params: CoreParams::default(),
}))
}
pub fn pipeline(
&mut self,
nodes: &[NodeId],
last: impl Into<GraphId>,
) -> Result<GraphId, GraphError> {
if nodes.is_empty() {
return Err(GraphError::Invalid("a pipeline needs at least one node"));
}
let mut next = last.into();
for &node in nodes.iter().rev() {
next = self.static_graph(node, &[next])?;
}
Ok(next)
}
pub fn parameterize_node(
&mut self,
node: impl Into<NodeId>,
params: LocalParams,
) -> Result<NodeId, GraphError> {
self.parameterize_node_with(node, NodeParams::default().with_params(params))
}
pub fn parameterize_node_with(
&mut self,
node: impl Into<NodeId>,
params: NodeParams,
) -> Result<NodeId, GraphError> {
let node = self.node_id(node.into())?;
let view = self
.registry
.node(node)
.map_err(|_| GraphError::UnknownId("node"))?;
let (spec, own, dict_id) = (view.spec, view.params.cloned(), view.dict_id);
Ok(NodeId(self.registry.add_node(NodeDef {
spec,
params: params.params.map(|params| params.0).or(own),
dict_id: params.dictionary.or(dict_id),
dict_index: None,
})))
}
pub fn load_dictionaries(&mut self, bundle: &DictBundle) -> Result<(), GraphError> {
self.registry
.load_bundle(bundle)
.map_err(|error| match error {
LoadError::AlreadyLoaded => GraphError::BundleAlreadyLoaded,
LoadError::Dict(error) => GraphError::Dictionary(error),
LoadError::NotFound(id) => GraphError::DictionaryNotFound(id),
})
}
pub fn parameterize_graph(
&mut self,
graph: impl Into<GraphId>,
overrides: GraphParams,
) -> Result<GraphId, GraphError> {
self.parameterize_graph_with(graph.into(), overrides, None)
}
fn parameterize_graph_with(
&mut self,
graph: GraphId,
overrides: GraphParams,
mparam: Option<RefParam>,
) -> Result<GraphId, GraphError> {
let base = self.graph_id(graph)?;
let (name, kind, inputs, last_variable, custom_graphs, custom_nodes, params) = {
let def = self
.registry
.graph(base)
.map_err(|_| GraphError::UnknownId("graph"))?;
let kind = match &def.kind {
GraphKind::Selector(Callback::Static(f)) => {
GraphKind::Selector(Callback::Static(*f))
}
GraphKind::Selector(Callback::Shared(f)) => {
GraphKind::Selector(Callback::Shared(Arc::clone(f)))
}
GraphKind::Function(Callback::Static(f)) => {
GraphKind::Function(Callback::Static(*f))
}
GraphKind::Function(Callback::Shared(f)) => {
GraphKind::Function(Callback::Shared(Arc::clone(f)))
}
GraphKind::Segmenter(Callback::Static(f)) => {
GraphKind::Segmenter(Callback::Static(*f))
}
GraphKind::Segmenter(Callback::Shared(f)) => {
GraphKind::Segmenter(Callback::Shared(Arc::clone(f)))
}
GraphKind::Static => GraphKind::Static,
GraphKind::Store => GraphKind::Store,
GraphKind::Illegal => GraphKind::Illegal,
};
(
def.name.clone(),
kind,
def.inputs.clone(),
def.last_variable,
def.custom_graphs.clone(),
def.custom_nodes.clone(),
def.params.clone(),
)
};
let custom_graphs = match overrides.custom_graphs {
Some(graphs) => Cow::Owned(
graphs
.iter()
.map(|&graph| self.graph_id(graph))
.collect::<Result<Vec<_>, _>>()?,
),
None => custom_graphs,
};
let custom_nodes = match overrides.custom_nodes {
Some(nodes) => Cow::Owned(
nodes
.iter()
.map(|&node| self.node_id(node))
.collect::<Result<Vec<_>, _>>()?,
),
None => custom_nodes,
};
let inherited = params
.refs
.iter()
.find(|(id, _)| *id == engine::exec::MPARAM_REF_ID)
.map(|(_, object)| object.clone());
let mut params = overrides.params.map_or(params, |params| params.0);
if let Some(mparam) = mparam.or(inherited) {
params
.refs
.retain(|(id, _)| *id != engine::exec::MPARAM_REF_ID);
params.refs.push((engine::exec::MPARAM_REF_ID, mparam));
}
Ok(self.add(GraphDef {
name: overrides.name.map_or(name, Cow::Owned),
kind,
inputs,
last_variable,
custom_graphs,
custom_nodes,
params,
}))
}
pub fn selector<S: Selector>(
&mut self,
name: &str,
selector: S,
input: TypeSet,
successors: &[GraphId],
) -> Result<GraphId, GraphError> {
let successors = successors
.iter()
.map(|&graph| self.graph_id(graph))
.collect::<Result<Vec<_>, _>>()?;
let callback: Arc<dyn reg::SelectFn> = Arc::new(UserSelector(selector));
Ok(self.add(GraphDef {
name: Cow::Owned(name.to_string()),
kind: GraphKind::Selector(Callback::Shared(callback)),
inputs: Cow::Owned(alloc::vec![input.0]),
last_variable: false,
custom_graphs: Cow::Owned(successors),
custom_nodes: Cow::Borrowed(&[]),
params: CoreParams::default(),
}))
}
pub fn function_graph<F: FunctionGraph>(
&mut self,
name: &str,
function: F,
inputs: &[TypeSet],
last_input_variable: bool,
successors: &[GraphId],
nodes: &[NodeId],
) -> Result<GraphId, GraphError> {
let masks = Self::masks(inputs)?;
let successors = successors
.iter()
.map(|&graph| self.graph_id(graph))
.collect::<Result<Vec<_>, _>>()?;
let nodes = nodes
.iter()
.map(|&node| self.node_id(node))
.collect::<Result<Vec<_>, _>>()?;
let callback: Arc<dyn reg::GraphFn> = Arc::new(UserGraph(function));
Ok(self.add(GraphDef {
name: Cow::Owned(name.to_string()),
kind: GraphKind::Function(Callback::Shared(callback)),
inputs: masks,
last_variable: last_input_variable,
custom_graphs: Cow::Owned(successors),
custom_nodes: Cow::Owned(nodes),
params: CoreParams::default(),
}))
}
pub fn segmenter<S: Segmenter>(
&mut self,
name: &str,
segmenter: S,
inputs: &[TypeSet],
successors: &[GraphId],
) -> Result<GraphId, GraphError> {
let masks = Self::masks(inputs)?;
let successors = successors
.iter()
.map(|&graph| self.graph_id(graph))
.collect::<Result<Vec<_>, _>>()?;
let callback: Arc<dyn reg::SegmentFn> = Arc::new(UserSegmenter(segmenter));
Ok(self.add(GraphDef {
name: Cow::Owned(name.to_string()),
kind: GraphKind::Segmenter(Callback::Shared(callback)),
inputs: masks,
last_variable: false,
custom_graphs: Cow::Owned(successors),
custom_nodes: Cow::Borrowed(&[]),
params: CoreParams::default(),
}))
}
pub fn serial_segmenter(
&mut self,
chunk: crate::ChunkSize,
successor: impl Into<GraphId>,
) -> Result<GraphId, GraphError> {
let params = GraphParams::default()
.with_graphs([successor.into()])
.with_params(LocalParams::default().with_int(
engine::graphs::SERIAL_CHUNK_BYTE_SIZE,
i32::try_from(usize::from(chunk)).unwrap_or(i32::MAX),
));
self.parameterize_graph(StandardGraph::SegmentSerial, params)
}
pub fn num_from_serial_segmenter(
&mut self,
width: usize,
chunk: crate::ChunkSize,
successor: Option<GraphId>,
) -> Result<GraphId, GraphError> {
let (base, default) = match width {
1 => (
StandardGraph::SegmentNum8FromSerial,
StandardGraph::InterpretNum8Compress,
),
2 => (
StandardGraph::SegmentNum16FromSerial,
StandardGraph::InterpretNum16Compress,
),
4 => (
StandardGraph::SegmentNum32FromSerial,
StandardGraph::InterpretNum32Compress,
),
8 => (
StandardGraph::SegmentNum64FromSerial,
StandardGraph::InterpretNum64Compress,
),
_ => {
return Err(GraphError::Invalid(
"the element width must be 1, 2, 4 or 8",
));
}
};
let params = GraphParams::default()
.with_graphs([successor.unwrap_or_else(|| default.into())])
.with_params(
LocalParams::default()
.with_int(engine::graphs::NUM_FROM_SERIAL_ELT_WIDTH, width as i32)
.with_int(
engine::graphs::NUM_FROM_SERIAL_CHUNK_BYTE_SIZE,
i32::try_from(usize::from(chunk)).unwrap_or(i32::MAX),
),
);
self.parameterize_graph(base, params)
}
pub fn sddl(
&mut self,
description: &[u8],
successor: impl Into<GraphId>,
) -> Result<GraphId, GraphError> {
if !engine::graphs::validate_sddl(description) {
return Err(GraphError::Invalid(
"the description is not a loadable SDDL program",
));
}
let params = GraphParams::default()
.with_graphs([successor.into()])
.with_params(
LocalParams::default()
.with_bytes(engine::graphs::SDDL_DESCRIPTION_PID, description.to_vec()),
);
self.parameterize_graph(StandardGraph::SimpleDataDescriptionLanguage, params)
}
pub fn sddl2(
&mut self,
bytecode: &[u8],
chunk_size: usize,
successor: Option<GraphId>,
) -> Result<GraphId, GraphError> {
if !bytecode.len().is_multiple_of(4) {
return Err(GraphError::Invalid(
"SDDL2 bytecode must be a whole number of 32-bit words",
));
}
let chunk_size = i32::try_from(chunk_size)
.map_err(|_| GraphError::Invalid("the SDDL2 chunk size exceeds i32::MAX"))?;
let mut local = LocalParams::default()
.with_bytes(engine::graphs::SDDL2_BYTECODE_PARAM, bytecode.to_vec());
if chunk_size != 0 {
local = local.with_int(engine::graphs::SDDL2_CHUNK_BYTE_SIZE_PARAM, chunk_size);
}
let mut params = GraphParams::default().with_params(local);
if let Some(successor) = successor {
params = params.with_graphs([successor]);
}
self.parameterize_graph(StandardGraph::SimpleDataDescriptionLanguageV2, params)
}
pub fn brute_force(&mut self, successors: &[GraphId]) -> Result<GraphId, GraphError> {
if successors.is_empty() {
return Err(GraphError::Invalid(
"brute force needs at least one successor",
));
}
self.parameterize_graph(
StandardGraph::BruteForce,
GraphParams::default().with_graphs(successors),
)
}
pub fn ml_selector(
&mut self,
config: &MlSelectorConfig,
successors: &[GraphId],
) -> Result<GraphId, GraphError> {
self.parameterize_graph_with(
StandardGraph::MlSelector.into(),
GraphParams::default().with_graphs(successors),
Some(RefParam(config.materialized())),
)
}
pub fn untrained_ml_selector(&mut self, successors: &[GraphId]) -> Result<GraphId, GraphError> {
let config = MlSelectorConfig::untrained(successors.len())
.map_err(|_| GraphError::Invalid("an ML selector needs at least two successors"))?;
self.ml_selector(&config, successors)
}
pub fn clustering(
&mut self,
config: &ClusteringConfig,
successors: &[GraphId],
) -> Result<GraphId, GraphError> {
let codecs: Vec<NodeId> = [
StandardNode::ConcatSerial,
StandardNode::ConcatStruct,
StandardNode::ConcatNum,
StandardNode::ConcatString,
]
.into_iter()
.map(NodeId::from)
.collect();
self.clustering_with_codecs(config, successors, &codecs)
}
pub fn clustering_with_codecs(
&mut self,
config: &ClusteringConfig,
successors: &[GraphId],
codecs: &[NodeId],
) -> Result<GraphId, GraphError> {
if config.needs().1 > codecs.len() {
return Err(GraphError::Invalid(
"a cluster names a clustering codec that does not exist",
));
}
let blob: Vec<u8> = config.into();
self.parameterize_graph(
StandardGraph::Clustering,
GraphParams::default()
.with_graphs(successors)
.with_nodes(codecs)
.with_params(LocalParams::default().with_bytes(CLUSTERING_CONFIG_PARAM, blob)),
)
}
pub fn build(mut self, start: impl Into<GraphId>) -> Result<CompressorGraph, GraphError> {
self.registry.start = self.graph_id(start.into())?;
self.registry
.resolve_dict_indices()
.map_err(|error| match error {
LoadError::NotFound(id) => GraphError::DictionaryNotFound(id),
LoadError::Dict(error) => GraphError::Dictionary(error),
LoadError::AlreadyLoaded => GraphError::BundleAlreadyLoaded,
})?;
Ok(CompressorGraph(Arc::new(self.registry)))
}
}
#[derive(Clone, Debug)]
pub struct CompressorGraph(pub(crate) Arc<Registry>);
impl Default for CompressorGraph {
fn default() -> Self {
Self(Arc::new(Registry::new(0, reg::gid::COMPRESS_GENERIC)))
}
}
impl CompressorGraph {
#[must_use]
pub fn standard(start: StandardGraph) -> Self {
Self(Arc::new(Registry::new(
0,
reg::GraphId::std(u32::from(start)),
)))
}
}
impl From<StandardGraph> for CompressorGraph {
fn from(start: StandardGraph) -> Self {
Self::standard(start)
}
}