use super::charset;
use crate::sink::{EncodeSink, SinkFull};
use crate::tags::{app_tag, encode_tag_to_sink, TagClass};
const MAX_HEADER: usize = 7;
fn write_tagged<S: EncodeSink + ?Sized>(
sink: &mut S,
tag: u8,
class: TagClass,
lead: &[u8],
payload: &[u8],
) -> Result<usize, SinkFull> {
let contents = lead.len().saturating_add(payload.len());
let length = header_length(sink, contents)?;
let mut header = [0; MAX_HEADER];
let mut tail = &mut header[..];
let header_len = encode_tag_to_sink(&mut tail, tag, class, length)
.expect("seven octets hold any tag header");
sink.write_parts(&[&header[..header_len], lead, payload])?;
Ok(header_len + contents)
}
fn header_length<S: EncodeSink + ?Sized>(sink: &S, contents: usize) -> Result<u32, SinkFull> {
u32::try_from(contents).map_err(|_| SinkFull {
needed: contents.saturating_add(MAX_HEADER),
available: sink.remaining_capacity().unwrap_or(u32::MAX as usize),
})
}
fn write_contents<S: EncodeSink + ?Sized>(
sink: &mut S,
lead: u8,
payload: &[u8],
) -> Result<usize, SinkFull> {
sink.write_parts(&[&[lead], payload])?;
Ok(1 + payload.len())
}
pub fn encode_character_string_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
value: &str,
) -> Result<usize, SinkFull> {
write_contents(sink, charset::UTF8, value.as_bytes())
}
pub fn encode_app_character_string_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
value: &str,
) -> Result<usize, SinkFull> {
write_tagged(
sink,
app_tag::CHARACTER_STRING,
TagClass::Application,
&[charset::UTF8],
value.as_bytes(),
)
}
pub fn encode_ctx_character_string_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
tag: u8,
value: &str,
) -> Result<usize, SinkFull> {
write_tagged(
sink,
tag,
TagClass::Context,
&[charset::UTF8],
value.as_bytes(),
)
}
pub fn encode_app_octet_string_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
data: &[u8],
) -> Result<usize, SinkFull> {
write_tagged(
sink,
app_tag::OCTET_STRING,
TagClass::Application,
&[],
data,
)
}
pub fn encode_ctx_octet_string_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
tag: u8,
data: &[u8],
) -> Result<usize, SinkFull> {
write_tagged(sink, tag, TagClass::Context, &[], data)
}
pub fn encode_bit_string_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
unused_bits: u8,
data: &[u8],
) -> Result<usize, SinkFull> {
write_contents(sink, unused_bits, data)
}
pub fn encode_app_bit_string_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
unused_bits: u8,
data: &[u8],
) -> Result<usize, SinkFull> {
write_tagged(
sink,
app_tag::BIT_STRING,
TagClass::Application,
&[unused_bits],
data,
)
}
pub fn encode_ctx_bit_string_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
tag: u8,
unused_bits: u8,
data: &[u8],
) -> Result<usize, SinkFull> {
write_tagged(sink, tag, TagClass::Context, &[unused_bits], data)
}
#[cfg(test)]
mod tests;