use base::prelude::*;
use io::{Read};
use core::{mem, cmp};
use base::{error};
use super::{Zone};
macro_rules! rd {
($ip:expr, $t:ty) => {
if $ip.len() < mem::size_of::<$t>() {
Err(error::InvalidSequence)
} else {
let mut v: $t = 0;
let _ = $ip.read(v.as_mut());
Ok(v.from_be())
}
}
}
fn read_u8(ip: &mut &[u8]) -> Result<u8> { rd!(ip, u8) }
fn read_i32(ip: &mut &[u8]) -> Result<i32> { rd!(ip, i32) }
fn read_i64(ip: &mut &[u8]) -> Result<i64> { rd!(ip, i64) }
trait TReader {
fn read_seconds(ip: &mut &[u8]) -> Result<i64>;
fn seconds_width() -> usize;
}
struct T32Reader;
impl TReader for T32Reader {
fn read_seconds(ip: &mut &[u8]) -> Result<i64> {
read_i32(ip).map(|v| v as i64)
}
fn seconds_width() -> usize { 4 }
}
struct T64Reader;
impl TReader for T64Reader {
fn read_seconds(ip: &mut &[u8]) -> Result<i64> {
read_i64(ip)
}
fn seconds_width() -> usize { 8 }
}
pub fn parse(ip: &mut &[u8]) -> Result<Zone> {
if ip.len() < 5 {
return Err(error::InvalidSequence);
}
if &ip[..4] != "TZif" {
return Err(error::InvalidSequence);
}
let version = match ip[4] {
0 => 1,
_ => 2,
};
if version > 1 {
try!(discard::<T32Reader>(ip));
parse_::<T64Reader>(ip)
} else {
parse_::<T32Reader>(ip)
}
}
fn consume(buf: &mut &[u8], n: usize) {
let min = cmp::min(buf.len(), n);
*buf = &buf[min..];
}
fn parse_<T: TReader>(ip: &mut &[u8]) -> Result<Zone> {
consume(ip, 20);
let is_utc_indicators = try!(read_i32(ip)) as usize;
let is_std_indicators = try!(read_i32(ip)) as usize;
let num_leap_seconds = try!(read_i32(ip)) as usize;
let num_transitions = try!(read_i32(ip)) as usize;
let num_states = try!(read_i32(ip)) as usize;
let abbr_bytes = try!(read_i32(ip)) as usize;
if num_states == 0 {
return Err(error::InvalidSequence);
}
let mut transitions = vec!();
let mut states = vec!();
let mut leap_seconds = vec!();
for _ in 0..num_transitions {
transitions.push((try!(T::read_seconds(ip)), 0));
}
for i in 0..num_transitions {
let state = try!(read_u8(ip)) as usize;
if state >= num_states {
return Err(error::InvalidSequence);
}
transitions[i].1 = state;
}
for _ in 0..num_states {
states.push((try!(read_i32(ip)) as i64, try!(read_u8(ip)) != 0));
consume(ip, 1);
}
consume(ip, abbr_bytes);
for _ in 0..num_leap_seconds {
leap_seconds.push((try!(T::read_seconds(ip)), try!(read_i32(ip)) as i64));
}
consume(ip, is_std_indicators);
consume(ip, is_utc_indicators);
Ok(Zone {
transitions: transitions,
states: states,
leap_seconds: leap_seconds,
})
}
fn discard<T: TReader>(ip: &mut &[u8]) -> Result {
consume(ip, 20);
let is_utc_indicators = try!(read_i32(ip)) as usize;
let is_std_indicators = try!(read_i32(ip)) as usize;
let num_leap_seconds = try!(read_i32(ip)) as usize;
let num_transitions = try!(read_i32(ip)) as usize;
let num_states = try!(read_i32(ip)) as usize;
let abbr_bytes = try!(read_i32(ip)) as usize;
let bytes = num_transitions * T::seconds_width()
+ num_transitions * 1
+ num_states * (4 + 1 + 1)
+ abbr_bytes * 1
+ num_leap_seconds * (T::seconds_width() + 4)
+ is_utc_indicators * 1
+ is_std_indicators * 1;
consume(ip, bytes);
Ok(())
}