use std::cmp::Reverse;
use std::collections::HashMap;
use integer_encoding::VarIntWriter as _;
use crate::decoder::{
ColumnType02, DataType02, DictionaryType, LayerLayout, LogicalEncoding, PhysicalEncoding,
Presence02, StreamMeta, StreamType,
};
use crate::encoder::geometry::encode02::encode_geometry02;
use crate::encoder::model::{StagedLayer, StreamCtx};
use crate::encoder::{
Codecs, Encoder, StagedId, StagedOptScalar, StagedProperty, write_stream_payload,
};
use crate::utils::BinarySerializer as _;
use crate::{MltError, MltResult};
#[derive(Debug)]
struct SharedPresence<'a> {
masks: Vec<&'a [bool]>,
index: HashMap<&'a [bool], u8>,
}
impl<'a> SharedPresence<'a> {
fn plan(id: &'a StagedId, properties: &'a [StagedProperty]) -> Self {
let mut groups: HashMap<&'a [bool], (usize, usize)> = HashMap::new();
for (column, mask) in column_masks(id, properties).enumerate() {
let group = groups.entry(mask).or_insert((0, column));
group.0 += 1;
}
let mut shared: Vec<(&'a [bool], usize, usize)> = groups
.into_iter()
.filter(|&(_, (count, _))| count > 1)
.map(|(mask, (count, first))| (mask, count, first))
.collect();
shared.sort_unstable_by_key(|&(_, count, first)| (Reverse(count), first));
shared.truncate(usize::from(LayerLayout::MAX_SHARED_PRESENCE));
shared.sort_unstable_by_key(|&(_, _, first)| first);
let masks: Vec<&'a [bool]> = shared.into_iter().map(|(mask, _, _)| mask).collect();
let index = masks
.iter()
.enumerate()
.map(|(i, &mask)| {
(
mask,
u8::try_from(i).expect("at most MAX_SHARED_PRESENCE masks"),
)
})
.collect();
Self { masks, index }
}
fn count(&self) -> u8 {
u8::try_from(self.masks.len()).expect("at most MAX_SHARED_PRESENCE masks")
}
fn nibble_for(&self, mask: &[bool]) -> Presence02 {
self.index
.get(mask)
.map_or(Presence02::Inline, |&i| Presence02::Shared(i))
}
fn write_to(&self, enc: &mut Encoder) {
for mask in &self.masks {
write_presence_bits(enc.data_mut(), mask);
}
}
}
fn column_masks<'a>(
id: &'a StagedId,
properties: &'a [StagedProperty],
) -> impl Iterator<Item = &'a [bool]> {
fn mask<T: Copy + PartialEq>(v: &StagedOptScalar<T>) -> &[bool] {
&v.presence
}
let id = match id {
StagedId::OptU32(v) => Some(mask(v)),
StagedId::OptU64(v) => Some(mask(v)),
StagedId::None | StagedId::U32(_) | StagedId::U64(_) => None,
};
let props = properties.iter().filter_map(|prop| {
use StagedProperty as D;
match prop {
D::OptBool(v) => Some(mask(v)),
D::OptI8(v) => Some(mask(v)),
D::OptU8(v) => Some(mask(v)),
D::OptI32(v) => Some(mask(v)),
D::OptU32(v) => Some(mask(v)),
D::OptI64(v) => Some(mask(v)),
D::OptU64(v) => Some(mask(v)),
D::OptF32(v) => Some(mask(v)),
D::OptF64(v) => Some(mask(v)),
_ => None,
}
});
id.into_iter().chain(props)
}
fn write_presence_bits(data: &mut Vec<u8>, bits: &[bool]) {
let start = data.len();
data.resize(start + bits.len().div_ceil(8), 0);
for (i, &bit) in bits.iter().enumerate() {
if bit {
data[start + i / 8] |= 1 << (i % 8);
}
}
}
pub(crate) fn encode_into02(
layer: StagedLayer,
mut enc: Encoder,
codecs: &mut Codecs,
) -> MltResult<Encoder> {
let StagedLayer {
name,
extent,
id,
geometry,
properties,
} = layer;
let feature_count = u32::try_from(geometry.feature_count())?;
enc.count_context = feature_count;
let shared = SharedPresence::plan(&id, &properties);
let geometry = encode_geometry02(geometry)?;
enc.data_mut()
.push(LayerLayout::new(geometry.layout, shared.count()).to_byte());
shared.write_to(&mut enc);
geometry.write_to(&mut enc, codecs)?;
let column_count = usize::from(!matches!(id, StagedId::None)) + properties.len();
enc.data_mut().write_varint(u32::try_from(column_count)?)?;
write_id02(&id, &shared, &mut enc, codecs)?;
for prop in &properties {
write_prop02(prop, &shared, &mut enc, codecs)?;
}
enc.write_header02(&name, extent.get(), feature_count)?;
Ok(enc)
}
fn begin_col02(
enc: &mut Encoder,
presence: Presence02,
typ: DataType02,
name: Option<&str>,
) -> MltResult<()> {
let data = enc.data_mut();
data.push(ColumnType02::new(presence, typ).to_byte());
debug_assert_eq!(typ.has_name(), name.is_some());
if let Some(name) = name {
data.write_string(name)?;
}
Ok(())
}
fn write_opt_col02<F>(
enc: &mut Encoder,
shared: &SharedPresence<'_>,
typ: DataType02,
name: Option<&str>,
presence: &[bool],
write_data: F,
) -> MltResult<()>
where
F: FnOnce(&mut Encoder) -> MltResult<()>,
{
let nibble = shared.nibble_for(presence);
begin_col02(enc, nibble, typ, name)?;
if nibble == Presence02::Inline {
write_presence_bits(enc.data_mut(), presence);
}
let popcount = u32::try_from(presence.iter().filter(|&&p| p).count())?;
let feature_count = enc.count_context;
enc.count_context = popcount;
let result = write_data(enc);
enc.count_context = feature_count;
result
}
fn write_bool_bitfield(enc: &mut Encoder, values: &[bool]) -> MltResult<()> {
let mut packed = Vec::with_capacity(values.len().div_ceil(8));
write_presence_bits(&mut packed, values);
let meta = StreamMeta::new2(
StreamType::Data(DictionaryType::None),
LogicalEncoding::None,
PhysicalEncoding::None,
values.len(),
)?;
write_stream_payload(enc, meta, false, &packed)
}
fn write_id02(
id: &StagedId,
shared: &SharedPresence<'_>,
enc: &mut Encoder,
codecs: &mut Codecs,
) -> MltResult<()> {
use DataType02 as DT;
use Presence02::AllPresent;
let ctx = StreamCtx::id(StreamType::Data(DictionaryType::None));
match id {
StagedId::None => Ok(()),
StagedId::U32(v) => {
begin_col02(enc, AllPresent, DT::Id, None)?;
codecs.write_int_stream(&v.values, &ctx, enc)
}
StagedId::OptU32(v) => write_opt_col02(enc, shared, DT::Id, None, &v.presence, |enc| {
codecs.write_int_stream(&v.values, &ctx, enc)
}),
StagedId::U64(v) => {
begin_col02(enc, AllPresent, DT::LongId, None)?;
codecs.write_int_stream(&v.values, &ctx, enc)
}
StagedId::OptU64(v) => write_opt_col02(enc, shared, DT::LongId, None, &v.presence, |enc| {
codecs.write_int_stream(&v.values, &ctx, enc)
}),
}
}
fn write_prop02(
prop: &StagedProperty,
shared: &SharedPresence<'_>,
enc: &mut Encoder,
codecs: &mut Codecs,
) -> MltResult<()> {
use DataType02 as DT;
use Presence02::AllPresent;
use StagedProperty as D;
macro_rules! scalar {
($ct:ident, $v:expr) => {{
begin_col02(enc, AllPresent, DT::$ct, Some(&$v.name))?;
codecs.write_int_stream(&$v.values, &StreamCtx::prop_data(&$v.name), enc)
}};
}
macro_rules! opt_scalar {
($ct:ident, $v:expr) => {{
write_opt_col02(enc, shared, DT::$ct, Some(&$v.name), &$v.presence, |enc| {
codecs.write_int_stream(&$v.values, &StreamCtx::prop_data(&$v.name), enc)
})
}};
}
macro_rules! opt_float {
($ct:ident, $v:expr) => {{
write_opt_col02(enc, shared, DT::$ct, Some(&$v.name), &$v.presence, |enc| {
codecs.write_float_stream(&$v.values, StreamType::Data(DictionaryType::None), enc)
})
}};
}
match prop {
D::Bool(v) => {
begin_col02(enc, AllPresent, DT::Bool, Some(&v.name))?;
write_bool_bitfield(enc, &v.values)
}
D::OptBool(v) => {
write_opt_col02(enc, shared, DT::Bool, Some(&v.name), &v.presence, |enc| {
write_bool_bitfield(enc, &v.values)
})
}
D::F32(v) => {
begin_col02(enc, AllPresent, DT::F32, Some(&v.name))?;
codecs.write_float_stream(&v.values, StreamType::Data(DictionaryType::None), enc)
}
D::OptF32(v) => opt_float!(F32, v),
D::F64(v) => {
begin_col02(enc, AllPresent, DT::F64, Some(&v.name))?;
codecs.write_float_stream(&v.values, StreamType::Data(DictionaryType::None), enc)
}
D::OptF64(v) => opt_float!(F64, v),
D::I8(v) => scalar!(I8, v),
D::OptI8(v) => opt_scalar!(I8, v),
D::U8(v) => scalar!(U8, v),
D::OptU8(v) => opt_scalar!(U8, v),
D::I32(v) => scalar!(I32, v),
D::OptI32(v) => opt_scalar!(I32, v),
D::U32(v) => scalar!(U32, v),
D::OptU32(v) => opt_scalar!(U32, v),
D::I64(v) => scalar!(I64, v),
D::OptI64(v) => opt_scalar!(I64, v),
D::U64(v) => scalar!(U64, v),
D::OptU64(v) => opt_scalar!(U64, v),
D::Str(_) | D::OptStr(_) => Err(MltError::NotImplemented("v2 string columns")),
D::SharedDict(_) => Err(MltError::NotImplemented("v2 shared dictionary columns")),
}
}