gled-core 2.34.0

The engine of gled without a user interface: inputs, timing, rendering, Art-Net/DMX output and storage
Documentation
//! ArtDmx packets, written straight into the buffer they are sent from:
//! the header and a universe's 512 channels, with no allocation per packet.

use gled_gpu::constants::UNIVERSE_BUFFER_SIZE;

/// The bytes before the channels: "Art-Net", the opcode, the protocol
/// version, sequence, physical port, port address and length.
const HEADER_LEN: usize = 18;
pub(super) const PACKET_LEN: usize = HEADER_LEN + UNIVERSE_BUFFER_SIZE as usize;
/// The highest Art-Net port address: net, sub-net and universe, 15 bits.
const MAX_PORT_ADDRESS: u16 = 0x7fff;

/// Appends the ArtDmx packet that sends `data` to `universe`. Returns
/// `false`, and appends nothing, for a universe Art-Net cannot address.
pub(super) fn write(buffer: &mut Vec<u8>, universe: u16, data: &[u8; 512]) -> bool {
    if universe > MAX_PORT_ADDRESS {
        return false;
    }
    let [port_low, port_high] = universe.to_le_bytes();
    let [length_high, length_low] = (UNIVERSE_BUFFER_SIZE as u16).to_be_bytes();
    buffer.extend_from_slice(&[
        b'A',
        b'r',
        b't',
        b'-',
        b'N',
        b'e',
        b't',
        0,
        // OpDmx, little-endian
        0x00,
        0x50,
        // protocol version 14
        0,
        14,
        // no sequence numbers, physical port 0
        0,
        0,
        port_low,
        port_high,
        length_high,
        length_low,
    ]);
    buffer.extend_from_slice(data);
    true
}

#[cfg(test)]
mod tests {
    use super::*;

    /// The packets are what the artnet_protocol crate writes, which gled
    /// sent before.
    #[test]
    fn a_packet_is_what_artnet_protocol_writes() {
        let mut data = [0u8; 512];
        for (index, value) in data.iter_mut().enumerate() {
            *value = (index * 7 % 256) as u8;
        }
        for universe in [0u16, 1, 255, 256, 4095, MAX_PORT_ADDRESS] {
            let mut buffer = Vec::new();
            assert!(write(&mut buffer, universe, &data));

            let expected = artnet_protocol::ArtCommand::Output(artnet_protocol::Output {
                data: artnet_protocol::PaddedData::from(data.to_vec()),
                port_address: artnet_protocol::PortAddress::try_from(universe)
                    .expect("a port address"),
                ..Default::default()
            })
            .write_to_buffer()
            .expect("an ArtDmx packet");
            assert_eq!(buffer, expected, "universe {universe}");
            assert_eq!(buffer.len(), PACKET_LEN);
        }
    }

    #[test]
    fn a_universe_past_the_port_addresses_writes_nothing() {
        let mut buffer = vec![1, 2, 3];
        assert!(!write(&mut buffer, MAX_PORT_ADDRESS + 1, &[0; 512]));
        assert_eq!(buffer, [1, 2, 3]);
    }
}