use std::io::{Write};
use snafu::{Snafu, ResultExt};
use std::collections::btree_map::{self, BTreeMap};
use std::convert::{TryFrom};
use std::cmp;
use serde::ser::{Serialize, Serializer, SerializeSeq, SerializeMap};
use crate::rwtfile::{DataField};
use crate::flagscolumn::{self, FlagsColumn};
use crate::utils::{write};
use crate::polyline::FieldEncodeOptions;
use crate::simplify::simplify_and_encode;
use crate::surface::SurfaceMapping;
#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("Column {} tried to change type", name))]
ColumnTypeChange{name: String},
#[snafu(display("Column {} tried to reused index {}", name, index))]
IndexAlreadyUsed{name: String, index: usize},
#[snafu(display("Couldn't write types table: {}", source))]
WriteTypesTable{source: std::io::Error},
#[snafu(display("Couldn't write column {}: {}", name, source))]
WriteDataColumn{name: String, source: std::io::Error},
#[snafu(display("Couldn't write flags column: {}", source))]
WriteFlagsColumn{source: flagscolumn::Error},
#[snafu(display("Couldn't write section header: {}", source))]
WriteHeader{source: std::io::Error},
#[snafu(display("Couldn't write: {}", source))]
WriteBytes{source: std::io::Error},
#[snafu(display("Couldn't write data column - number of points"))]
WriteDataColumnNumberOfPoints{},
#[snafu(display("Number truncation error: {}", source))]
NumberTruncation{source: std::num::TryFromIntError},
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug)]
pub enum Column {
Numbers(BTreeMap<usize, i64>),
LongFloat(BTreeMap<usize, f64>),
ShortFloat(BTreeMap<usize, f64>),
Base64(BTreeMap<usize, Vec<u8>>),
String(BTreeMap<usize, String>),
Bool(BTreeMap<usize, bool>),
IDs(BTreeMap<usize, Vec<u64>>),
}
impl Column {
fn type_tag(&self) -> u8 {
match self {
Column::Numbers(_) => 0x00,
Column::LongFloat(_) => 0x01,
Column::ShortFloat(_) => 0x02,
Column::Base64(_) => 0x03,
Column::String(_) => 0x04,
Column::Bool(_) => 0x05,
Column::IDs(_) => 0x06,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SectionType {
TrackPoints,
CoursePoints,
Continuation,
}
impl SectionType {
pub(crate) fn from_tag(tag: u8) -> Option<Self> {
match tag {
0x00 => Some(SectionType::TrackPoints),
0x01 => Some(SectionType::CoursePoints),
0x02 => Some(SectionType::Continuation),
_ => None
}
}
fn type_tag(&self) -> u8 {
match self {
SectionType::TrackPoints => 0x00,
SectionType::CoursePoints => 0x01,
SectionType::Continuation => 0x02,
}
}
}
#[derive(Debug)]
pub struct Section {
pub(crate) section_type: SectionType,
pub(crate) max: usize,
pub(crate) flags: FlagsColumn,
pub(crate) columns: BTreeMap<String, Column>,
}
macro_rules! add_x {
($name: ident, $variant: path, $type: ty) => {
pub(crate) fn $name(&mut self, index: usize, k: &str, v: $type) -> Result<()> {
match self.columns.get_mut(k) {
Some(column) => {
match column {
$variant(m) => {
match m.entry(index) {
btree_map::Entry::Vacant(inner_entry) => {
inner_entry.insert(v);
self.max = cmp::max(self.max, index); self.flags.set(index, k);
Ok(())
},
btree_map::Entry::Occupied(_) => {
IndexAlreadyUsed{name: k,
index: index}.fail()
},
}
},
_ => {
ColumnTypeChange{name: k}.fail()
}
}
},
None => {
let mut m = BTreeMap::new();
m.insert(index, v);
self.columns.insert(k.into(), $variant(m));
self.max = cmp::max(self.max, index);
self.flags.set(index, k);
Ok(())
}
}
}
}
}
impl Section {
pub(crate) fn new(section_type: SectionType) -> Self {
Section{section_type: section_type,
max: 0,
flags: FlagsColumn::new(),
columns: BTreeMap::new()}
}
add_x!(add_number, Column::Numbers, i64);
add_x!(add_long_float, Column::LongFloat, f64);
add_x!(add_short_float, Column::ShortFloat, f64);
add_x!(add_base64, Column::Base64, Vec<u8>);
add_x!(add_string, Column::String, String);
add_x!(add_bool, Column::Bool, bool);
add_x!(add_ids, Column::IDs, Vec<u64>);
pub fn len(&self) -> usize {
self.flags.len()
}
pub(crate) fn type_tag(&self) -> u8 {
self.section_type.type_tag()
}
pub fn columns(&self) -> &BTreeMap<String, Column> {
&self.columns
}
pub fn simplify_and_encode(&self, mapping: &SurfaceMapping, tolerance: f64, fields: &[FieldEncodeOptions]) -> String {
simplify_and_encode(self, mapping, tolerance, fields)
}
fn write_types_table<W: Write>(&self, out: &mut W) -> Result<usize> {
let mut buf = Vec::new();
write(&mut buf, &u8::try_from(self.columns.len()).context(NumberTruncation{})?.to_le_bytes()).context(WriteTypesTable{})?;
for name in self.flags.fields() {
if let Some(column) = self.columns.get(name) {
write(&mut buf, &column.type_tag().to_le_bytes()).context(WriteTypesTable{})?;
write(&mut buf, &u8::try_from(name.len()).context(NumberTruncation{})?.to_le_bytes()).context(WriteTypesTable{})?;
write(&mut buf, name.as_bytes()).context(WriteTypesTable{})?;
} else {
panic!("TODO")
}
}
let crc = crc::crc16::checksum_usb(&buf).to_le_bytes();
write(&mut buf, &crc).context(WriteTypesTable{})?;
let written = write(out, &buf).context(WriteTypesTable{})?;
Ok(written)
}
fn write_data<W: Write>(&self, out: &mut W) -> Result<usize> {
let mut buf = Vec::new();
self.flags.write(&mut buf).context(WriteFlagsColumn)?;
for name in self.flags.fields() {
if let Some(column) = self.columns.get(name) {
match column {
Column::Numbers(m) => {
let mut last = 0;
for index in 0..=self.max {
let delta = match m.get(&index) {
Some(v) => {
let value = *v;
let delta = value - last;
last = value;
delta
}
None => 0
};
leb128::write::signed(&mut buf, delta).with_context(|| WriteDataColumn{name: name.clone()})?;
}
}
Column::LongFloat(m) => {
let mut last = 0;
for index in 0..=self.max {
let delta = match m.get(&index) {
Some(v) => {
let value = (*v * 10000000.0) as i64;
let delta = value - last;
last = value;
delta
}
None => 0
};
leb128::write::signed(&mut buf, delta).with_context(|| WriteDataColumn{name: name.clone()})?;
}
}
Column::ShortFloat(m) => {
let mut last = 0;
for index in 0..=self.max {
let delta = match m.get(&index) {
Some(v) => {
let value = (*v * 1000.0) as i64;
let delta = value - last;
last = value;
delta
}
None => 0
};
leb128::write::signed(&mut buf, delta).with_context(|| WriteDataColumn{name: name.clone()})?;
}
}
Column::Base64(m) => {
for index in 0..=self.max {
let empty = Vec::with_capacity(0);
let v = m.get(&index).unwrap_or(&empty);
leb128::write::unsigned(&mut buf, u64::try_from(v.len()).context(NumberTruncation{})?).with_context(|| WriteDataColumn{name: name.clone()})?;
write(&mut buf, &v).with_context(|| WriteDataColumn{name: name.clone()})?;
}
}
Column::String(m) => {
let empty = "".to_string();
for index in 0..=self.max {
let v = m.get(&index).unwrap_or(&empty);
leb128::write::unsigned(&mut buf, u64::try_from(v.len()).context(NumberTruncation{})?).with_context(|| WriteDataColumn{name: name.clone()})?;
write(&mut buf, v.as_bytes()).with_context(|| WriteDataColumn{name: name.clone()})?;
}
}
Column::Bool(m) => {
for index in 0..=self.max {
let b = m.get(&index).unwrap_or(&false);
let v = *b as u8;
write(&mut buf, &v.to_le_bytes()).with_context(|| WriteDataColumn{name: name.clone()})?;
}
}
Column::IDs(m) => {
let empty = Vec::with_capacity(0);
for index in 0..=self.max {
let v = m.get(&index).unwrap_or(&empty);
leb128::write::unsigned(&mut buf, u64::try_from(v.len()).context(NumberTruncation{})?).with_context(|| WriteDataColumn{name: name.clone()})?;
for id in v {
leb128::write::unsigned(&mut buf, *id).with_context(|| WriteDataColumn{name: name.clone()})?;
}
}
}
}
} else {
panic!("TODO")
}
}
let crc = crc::crc32::checksum_ieee(&buf).to_le_bytes();
write(&mut buf, &crc).with_context(|| WriteDataColumn{name: "crc"})?;
let written = write(out, &buf).with_context(|| WriteDataColumn{name: "full"})?;
Ok(written)
}
fn write_header<W: Write>(&self, out: &mut W, section_size: u64) -> Result<usize> {
let mut buf = Vec::new();
write(&mut buf, &self.type_tag().to_le_bytes()).context(WriteHeader{})?;
let len = self.len();
if len < 2usize.pow(24) {
write(&mut buf, &len.to_le_bytes()[..3]).context(WriteHeader{})?;
} else {
WriteDataColumnNumberOfPoints{}.fail()?;
}
write(&mut buf, §ion_size.to_le_bytes()).context(WriteHeader{})?;
let crc = crc::crc16::checksum_usb(&buf).to_le_bytes();
write(&mut buf, &crc).context(WriteHeader{})?;
let written = write(out, &buf).context(WriteHeader{})?;
Ok(written)
}
pub fn write<W: Write>(&self, out: &mut W) -> Result<usize> {
let mut written = 0;
let mut buf = Vec::new();
if self.len() > 0 {
written += self.write_types_table(&mut buf)?;
written += self.write_data(&mut buf)?;
}
let header_size: u64 = 12;
let data_size = u64::try_from(buf.len()).context(NumberTruncation{})?;
written += self.write_header(out, header_size + data_size)?;
written += write(out, &buf).context(WriteBytes{})?;
Ok(written)
}
}
pub struct Point<'a> {
section: &'a Section,
index: usize,
}
impl<'a> Point<'a> {
fn new(section: &'a Section, index: usize) -> Self {
Point{section,
index}
}
}
impl<'a> Serialize for Point<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
for field in self.section.flags.fields.keys() {
if let Some(column) = self.section.columns.get(field) {
let maybe_data = match column {
Column::Numbers(m) => m.get(&self.index).map(|v| DataField::Number(*v)),
Column::LongFloat(m) => m.get(&self.index).map(|v| DataField::LongFloat(*v)),
Column::ShortFloat(m) => m.get(&self.index).map(|v| DataField::ShortFloat(*v)),
Column::Base64(m) => m.get(&self.index).map(|v| DataField::Base64(base64::encode(v))),
Column::String(m) => m.get(&self.index).map(|v| DataField::String(v.to_string())),
Column::Bool(m) => m.get(&self.index).map(|v| DataField::Bool(*v)),
Column::IDs(m) => m.get(&self.index).map(|v| DataField::IDs(v.to_vec())),
};
if let Some(data) = maybe_data {
map.serialize_entry(field, &data)?;
}
}
}
map.end()
}
}
impl Serialize for Section {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let len = self.len();
let mut seq = serializer.serialize_seq(Some(len))?;
for i in 0..len {
seq.serialize_element(&Point::new(&self, i))?;
}
seq.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::polyline::PointField;
#[test]
fn test_max() {
let mut s = Section::new(SectionType::TrackPoints);
for i in 0..=20 {
assert!(s.add_number(i, "foo", 0).is_ok());
}
assert_eq!(s.max, 20);
for i in 0..=20 {
assert!(s.add_base64(i, "bar", vec![1,2,3]).is_ok());
}
assert_eq!(s.max, 20);
assert!(s.add_base64(302, "bar", vec![1,2,3]).is_ok());
assert_eq!(s.max, 302);
}
#[test]
fn test_cant_overwrite() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_number(0, "foo", 0).is_ok());
assert!(s.add_number(0, "foo", 0).is_err());
}
#[test]
fn test_cant_change_column_type() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_number(0, "foo", 0).is_ok());
assert!(s.add_base64(1, "foo", vec![1]).is_err());
assert!(s.add_short_float(2, "foo", 0.0).is_err());
assert!(s.add_number(3, "foo", 0).is_ok());
}
#[test]
fn test_len() {
let mut s = Section::new(SectionType::TrackPoints);
assert_eq!(s.len(), 0);
assert!(s.add_number(0, "foo", 0).is_ok());
assert_eq!(s.len(), 1);
assert!(s.add_number(500, "foo", 0).is_ok());
assert_eq!(s.len(), 501);
}
#[test]
fn test_write_types_table() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_number(1, "foo", 5).is_ok());
assert!(s.add_base64(1, "bazar", vec![0,1,2,3,4]).is_ok());
let mut buf = vec![];
let written = s.write_types_table(&mut buf);
assert!(written.is_ok());
let expected = &[0x02, 0x00, 0x03, b'f', b'o', b'o',
0x03, 0x05, b'b', b'a', b'z', b'a', b'r',
0xE5, 0x24];
assert_eq!(buf, expected);
assert_eq!(written.unwrap(), expected.len());
}
#[test]
fn test_write_large_types_table() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_number(0, "a", 5).is_ok());
assert!(s.add_number(1, "a", 7).is_ok());
assert!(s.add_number(0, "b", 5).is_ok());
assert!(s.add_base64(0, "c", vec![0]).is_ok());
assert!(s.add_base64(0, "d", vec![]).is_ok());
assert!(s.add_base64(0, "e_long_name", vec![0]).is_ok());
assert!(s.add_short_float(0, "f", 0.1).is_ok());
assert!(s.add_long_float(0, "g", 0.2).is_ok());
assert!(s.add_number(500, "h", 10).is_ok());
assert!(s.add_number(500, "i", 11).is_ok());
assert!(s.add_number(500, "j10", 12).is_ok());
let mut buf = vec![];
let written = s.write_types_table(&mut buf);
assert!(written.is_ok());
let expected = vec![0x0A, 0x00, 0x01, b'a',
0x00, 0x01, b'b',
0x03, 0x01, b'c',
0x03, 0x01, b'd',
0x03, 0x0B, b'e', b'_', b'l', b'o', b'n', b'g', b'_', b'n', b'a', b'm', b'e',
0x02, 0x01, b'f',
0x01, 0x01, b'g',
0x00, 0x01, b'h',
0x00, 0x01, b'i',
0x00, 0x03, b'j', b'1', b'0',
0x87, 0x12];
assert_eq!(buf, expected);
assert_eq!(written.unwrap(), expected.len());
}
#[test]
fn test_type_with_multibyte_character() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_number(1, "I♥NY", 5).is_ok());
let mut buf = vec![];
let written = s.write_types_table(&mut buf);
assert!(written.is_ok());
let expected = &[0x01, 0x00, 0x06, 0x49, 0xE2, 0x99,
0xA5,
0x4E, 0x59, 0xA3, 0xF5];
assert_eq!(buf, expected);
assert_eq!(written.unwrap(), expected.len());
}
#[test]
fn test_write_header_empty() {
let s = Section::new(SectionType::TrackPoints);
let mut buf = vec![];
let written = s.write_header(&mut buf, 10);
assert!(written.is_ok());
let expected = &[0x00, 0x00, 0x00,
0x00,
0x0A, 0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x1B, 0x82];
assert_eq!(buf, expected);
assert_eq!(written.unwrap(), expected.len());
}
#[test]
fn test_write_header_with_points() {
let mut s = Section::new(SectionType::CoursePoints);
assert!(s.add_number(100, "a", 20).is_ok());
let mut buf = vec![];
let written = s.write_header(&mut buf, 10);
assert!(written.is_ok());
let expected = &[0x01, 0x65, 0x00,
0x00,
0x0A, 0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x09, 0x8C];
assert_eq!(buf, expected);
assert_eq!(written.unwrap(), expected.len());
}
#[test]
fn test_write_data_single_column() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_number(0, "a", 20).is_ok());
assert!(s.add_number(1, "a", 25).is_ok());
assert!(s.add_number(2, "a", 28).is_ok());
assert!(s.add_number(3, "a", 10).is_ok());
assert!(s.add_number(6, "a", 12).is_ok());
let mut buf = vec![];
let written = s.write_data(&mut buf);
assert!(written.is_ok());
let expected = &[0x01, 0x01,
0x01,
0x01,
0x00,
0x00,
0x01,
20, 5, 3, 0x6E, 0, 0, 2, 0xA7, 0x60,
0xA8,
0xB6];
assert_eq!(buf, expected);
assert_eq!(written.unwrap(), expected.len());
}
#[test]
fn test_write_data_two_columns() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_number(0, "a", 20).is_ok());
assert!(s.add_number(0, "b", 42).is_ok());
assert!(s.add_number(1, "a", 25).is_ok());
assert!(s.add_number(1, "b", 52).is_ok());
let mut buf = vec![];
let written = s.write_data(&mut buf);
assert!(written.is_ok());
let expected = &[0x03, 0x03,
20, 5, 42, 10, 0xC8, 0x8E,
0xF8,
0x26];
assert_eq!(buf, expected);
assert_eq!(written.unwrap(), expected.len());
}
#[test]
fn test_write_data_zeros_for_missing_values() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_number(10, "a", 20).is_ok());
let mut buf = vec![];
let written = s.write_data(&mut buf);
assert!(written.is_ok());
let expected = &[0x00, 0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x01,
0x00, 0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
20, 0xF2, 0x29,
0x56,
0x29];
assert_eq!(buf, expected);
assert_eq!(written.unwrap(), expected.len());
}
#[test]
fn test_write_data_base64() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_base64(0, "a", "foo".as_bytes().to_vec()).is_ok());
assert!(s.add_base64(1, "a", "bazar".as_bytes().to_vec()).is_ok());
let mut buf = vec![];
let written = s.write_data(&mut buf);
assert!(written.is_ok());
let expected = &[0x01, 0x01,
0x03, b'f',
b'o',
b'o',
0x05, b'b',
b'a',
b'z',
b'a',
b'r',
0x7E, 0x1A,
0xAB,
0x3A];
assert_eq!(buf, expected);
assert_eq!(written.unwrap(), expected.len());
}
#[test]
fn test_write_data_numbers_and_base64() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_base64(0, "a", "foo".as_bytes().to_vec()).is_ok());
assert!(s.add_base64(1, "a", "bazar".as_bytes().to_vec()).is_ok());
assert!(s.add_number(0, "b", 42).is_ok());
assert!(s.add_number(1, "b", 45).is_ok());
assert!(s.add_number(2, "b", 50).is_ok());
let mut buf = vec![];
let written = s.write_data(&mut buf);
assert!(written.is_ok());
let expected = &[0x03, 0x03,
0x02,
0x03, b'f',
b'o',
b'o',
0x05, b'b',
b'a',
b'z',
b'a',
b'r',
0x00, 42,
3,
5,
0x89, 0x58,
0x64,
0xBE];
assert_eq!(buf, expected);
assert_eq!(written.unwrap(), expected.len());
}
#[test]
fn test_simplify_empty_section() {
let s = Section::new(SectionType::TrackPoints);
let mapping = SurfaceMapping::new(95);
let fields = vec![
FieldEncodeOptions::new(PointField::Y, 5),
FieldEncodeOptions::new(PointField::X, 5),
];
assert_eq!(s.simplify_and_encode(&mapping, 0.0, &fields), "");
}
#[test]
fn test_simplify_basic_section() {
let mut s = Section::new(SectionType::TrackPoints);
assert!(s.add_long_float(0, "x", 1.0).is_ok());
assert!(s.add_long_float(0, "y", 1.0).is_ok());
assert!(s.add_long_float(0, "e", 1.0).is_ok());
assert!(s.add_long_float(1, "x", -122.07012).is_ok());
assert!(s.add_long_float(1, "y", 44.000002).is_ok());
assert!(s.add_long_float(1, "e", 1.0).is_ok());
let mapping = SurfaceMapping::new(95);
let fields = vec![
FieldEncodeOptions::new(PointField::Y, 5),
FieldEncodeOptions::new(PointField::X, 5),
];
assert_eq!(s.simplify_and_encode(&mapping, 0.0, &fields), "_ibE_ibE_mmeGfcdnV");
}
}