conspire 0.7.7

The Rust interface to conspire.
Documentation
#[cfg(test)]
mod test;

use super::{
    AttValue, Attribute, DimSpec, NC_ATTRIBUTE, NC_CHAR_TYPE, NC_DIMENSION, NC_FLOAT, NC_INT,
    NC_VARIABLE, VarSpec, type_size,
};

fn pad4(n: u64) -> u64 {
    n.div_ceil(4) * 4
}

struct HeaderWriter {
    buf: Vec<u8>,
}

impl HeaderWriter {
    fn tag(&mut self, v: i32) {
        self.buf.extend_from_slice(&v.to_be_bytes());
    }
    fn i64(&mut self, v: i64) {
        self.buf.extend_from_slice(&v.to_be_bytes());
    }
    fn raw(&mut self, b: &[u8]) {
        self.buf.extend_from_slice(b);
    }
    fn align(&mut self) {
        while !self.buf.len().is_multiple_of(4) {
            self.buf.push(0);
        }
    }
    fn name(&mut self, s: &str) {
        self.i64(s.len() as i64);
        self.raw(s.as_bytes());
        self.align();
    }
    fn att_list(&mut self, atts: &[Attribute]) {
        if atts.is_empty() {
            self.tag(0);
            self.i64(0);
            return;
        }
        self.tag(NC_ATTRIBUTE);
        self.i64(atts.len() as i64);
        for att in atts {
            self.name(&att.name);
            match &att.value {
                AttValue::Int(xs) => {
                    self.tag(NC_INT);
                    self.i64(xs.len() as i64);
                    for &x in xs {
                        self.raw(&x.to_be_bytes());
                    }
                }
                AttValue::Float(xs) => {
                    self.tag(NC_FLOAT);
                    self.i64(xs.len() as i64);
                    for &x in xs {
                        self.raw(&x.to_bits().to_be_bytes());
                    }
                }
                AttValue::Text(s) => {
                    self.tag(NC_CHAR_TYPE);
                    self.i64(s.len() as i64);
                    self.raw(s.as_bytes());
                }
            }
            self.align();
        }
    }
}

fn encode_header(dims: &[DimSpec], gatts: &[Attribute], vars: &[VarSpec]) -> Vec<u8> {
    let mut w = HeaderWriter {
        buf: Vec::with_capacity(512),
    };
    w.raw(b"CDF");
    w.buf.push(5);
    w.i64(0);

    if dims.is_empty() {
        w.tag(0);
        w.i64(0);
    } else {
        w.tag(NC_DIMENSION);
        w.i64(dims.len() as i64);
        for dim in dims {
            w.name(&dim.name);
            w.i64(dim.len as i64);
        }
    }

    w.att_list(gatts);

    if vars.is_empty() {
        w.tag(0);
        w.i64(0);
    } else {
        w.tag(NC_VARIABLE);
        w.i64(vars.len() as i64);
        for var in vars {
            w.name(&var.name);
            w.i64(var.dimids.len() as i64);
            for &d in &var.dimids {
                w.i64(d as i64);
            }
            w.att_list(&var.atts);
            w.tag(var.xtype);
            w.i64(var.vsize as i64);
            w.i64(var.begin as i64);
        }
    }
    w.buf
}

pub(in crate::io::netcdf) fn finalize(
    dims: &[DimSpec],
    gatts: &[Attribute],
    vars: &mut [VarSpec],
) -> Vec<u8> {
    for var in vars.iter_mut() {
        var.vsize = pad4(var.elements(dims) * type_size(var.xtype) as u64);
    }
    let header_len = encode_header(dims, gatts, vars).len() as u64;
    let mut offset = header_len;
    for var in vars.iter_mut() {
        var.begin = offset;
        offset += var.vsize;
    }
    encode_header(dims, gatts, vars)
}