diff options
| author | Tim Chevalier <chevalier@alum.wellesley.edu> | 2012-11-24 13:38:23 -0800 |
|---|---|---|
| committer | Tim Chevalier <chevalier@alum.wellesley.edu> | 2012-11-24 13:40:00 -0800 |
| commit | ec0c029a7ed9ac34bec716193e51b15d29965b0e (patch) | |
| tree | f014c5482f1faf04b912f2f08423fe1c070aa8ad /src/libstd | |
| parent | 236fd32f0a6647997a68dc729460a1030fdc74cb (diff) | |
Split EBML module into a reader and a writer module
Minor refactoring, no review. Closes #2739
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/ebml.rs | 940 |
1 files changed, 474 insertions, 466 deletions
diff --git a/src/libstd/ebml.rs b/src/libstd/ebml.rs index 79e491e309b..2d413b404c5 100644 --- a/src/libstd/ebml.rs +++ b/src/libstd/ebml.rs @@ -5,6 +5,7 @@ use serialization; // cursor model. See the specification here: // http://www.matroska.org/technical/specs/rfc/index.html +// Common data structures struct EbmlTag { id: uint, size: uint, @@ -16,10 +17,6 @@ struct EbmlState { data_pos: uint, } -// FIXME (#2739): When we have module renaming, make "reader" and "writer" -// separate modules within this file. - -// ebml reading struct Doc { data: @~[u8], start: uint, @@ -31,592 +28,603 @@ struct TaggedDoc { doc: Doc, } -impl Doc: ops::Index<uint,Doc> { - pure fn index(tag: uint) -> Doc { - unsafe { - get_doc(self, tag) - } - } -} +enum EbmlSerializerTag { + EsUint, EsU64, EsU32, EsU16, EsU8, + EsInt, EsI64, EsI32, EsI16, EsI8, + EsBool, + EsStr, + EsF64, EsF32, EsFloat, + EsEnum, EsEnumVid, EsEnumBody, + EsVec, EsVecLen, EsVecElt, -fn vuint_at(data: &[u8], start: uint) -> {val: uint, next: uint} { - let a = data[start]; - if a & 0x80u8 != 0u8 { - return {val: (a & 0x7fu8) as uint, next: start + 1u}; - } - if a & 0x40u8 != 0u8 { - return {val: ((a & 0x3fu8) as uint) << 8u | - (data[start + 1u] as uint), - next: start + 2u}; - } else if a & 0x20u8 != 0u8 { - return {val: ((a & 0x1fu8) as uint) << 16u | - (data[start + 1u] as uint) << 8u | - (data[start + 2u] as uint), - next: start + 3u}; - } else if a & 0x10u8 != 0u8 { - return {val: ((a & 0x0fu8) as uint) << 24u | - (data[start + 1u] as uint) << 16u | - (data[start + 2u] as uint) << 8u | - (data[start + 3u] as uint), - next: start + 4u}; - } else { error!("vint too big"); fail; } -} + EsOpaque, -pub fn Doc(data: @~[u8]) -> Doc { - Doc { data: data, start: 0u, end: vec::len::<u8>(*data) } + EsLabel // Used only when debugging } +// -------------------------------------- + +pub mod Reader { + + // ebml reading -pub fn doc_at(data: @~[u8], start: uint) -> TaggedDoc { - let elt_tag = vuint_at(*data, start); - let elt_size = vuint_at(*data, elt_tag.next); - let end = elt_size.next + elt_size.val; - TaggedDoc { - tag: elt_tag.val, - doc: Doc { data: data, start: elt_size.next, end: end } + impl Doc: ops::Index<uint,Doc> { + pure fn index(tag: uint) -> Doc { + unsafe { + get_doc(self, tag) + } + } } -} -pub fn maybe_get_doc(d: Doc, tg: uint) -> Option<Doc> { - let mut pos = d.start; - while pos < d.end { - let elt_tag = vuint_at(*d.data, pos); - let elt_size = vuint_at(*d.data, elt_tag.next); - pos = elt_size.next + elt_size.val; - if elt_tag.val == tg { - return Some(Doc { data: d.data, start: elt_size.next, end: pos }); + fn vuint_at(data: &[u8], start: uint) -> {val: uint, next: uint} { + let a = data[start]; + if a & 0x80u8 != 0u8 { + return {val: (a & 0x7fu8) as uint, next: start + 1u}; + } + if a & 0x40u8 != 0u8 { + return {val: ((a & 0x3fu8) as uint) << 8u | + (data[start + 1u] as uint), + next: start + 2u}; + } else if a & 0x20u8 != 0u8 { + return {val: ((a & 0x1fu8) as uint) << 16u | + (data[start + 1u] as uint) << 8u | + (data[start + 2u] as uint), + next: start + 3u}; + } else if a & 0x10u8 != 0u8 { + return {val: ((a & 0x0fu8) as uint) << 24u | + (data[start + 1u] as uint) << 16u | + (data[start + 2u] as uint) << 8u | + (data[start + 3u] as uint), + next: start + 4u}; + } else { error!("vint too big"); fail; } + } + + pub fn Doc(data: @~[u8]) -> Doc { + Doc { data: data, start: 0u, end: vec::len::<u8>(*data) } + } + + pub fn doc_at(data: @~[u8], start: uint) -> TaggedDoc { + let elt_tag = vuint_at(*data, start); + let elt_size = vuint_at(*data, elt_tag.next); + let end = elt_size.next + elt_size.val; + TaggedDoc { + tag: elt_tag.val, + doc: Doc { data: data, start: elt_size.next, end: end } } } - None -} -pub fn get_doc(d: Doc, tg: uint) -> Doc { - match maybe_get_doc(d, tg) { - Some(d) => d, - None => { - error!("failed to find block with tag %u", tg); - fail; - } + pub fn maybe_get_doc(d: Doc, tg: uint) -> Option<Doc> { + let mut pos = d.start; + while pos < d.end { + let elt_tag = vuint_at(*d.data, pos); + let elt_size = vuint_at(*d.data, elt_tag.next); + pos = elt_size.next + elt_size.val; + if elt_tag.val == tg { + return Some(Doc { data: d.data, start: elt_size.next, end: pos }); + } + } + None } -} -pub fn docs(d: Doc, it: fn(uint, Doc) -> bool) { - let mut pos = d.start; - while pos < d.end { - let elt_tag = vuint_at(*d.data, pos); - let elt_size = vuint_at(*d.data, elt_tag.next); - pos = elt_size.next + elt_size.val; - let doc = Doc { data: d.data, start: elt_size.next, end: pos }; - if !it(elt_tag.val, doc) { - break; + pub fn get_doc(d: Doc, tg: uint) -> Doc { + match maybe_get_doc(d, tg) { + Some(d) => d, + None => { + error!("failed to find block with tag %u", tg); + fail; + } } } -} -pub fn tagged_docs(d: Doc, tg: uint, it: fn(Doc) -> bool) { - let mut pos = d.start; - while pos < d.end { - let elt_tag = vuint_at(*d.data, pos); - let elt_size = vuint_at(*d.data, elt_tag.next); - pos = elt_size.next + elt_size.val; - if elt_tag.val == tg { + pub fn docs(d: Doc, it: fn(uint, Doc) -> bool) { + let mut pos = d.start; + while pos < d.end { + let elt_tag = vuint_at(*d.data, pos); + let elt_size = vuint_at(*d.data, elt_tag.next); + pos = elt_size.next + elt_size.val; let doc = Doc { data: d.data, start: elt_size.next, end: pos }; - if !it(doc) { + if !it(elt_tag.val, doc) { break; } } } -} - -pub fn doc_data(d: Doc) -> ~[u8] { vec::slice::<u8>(*d.data, d.start, d.end) } - -pub fn with_doc_data<T>(d: Doc, f: fn(x: &[u8]) -> T) -> T { - f(vec::view(*d.data, d.start, d.end)) -} - -pub fn doc_as_str(d: Doc) -> ~str { str::from_bytes(doc_data(d)) } - -pub fn doc_as_u8(d: Doc) -> u8 { - assert d.end == d.start + 1u; - (*d.data)[d.start] -} - -pub fn doc_as_u16(d: Doc) -> u16 { - assert d.end == d.start + 2u; - io::u64_from_be_bytes(*d.data, d.start, 2u) as u16 -} - -pub fn doc_as_u32(d: Doc) -> u32 { - assert d.end == d.start + 4u; - io::u64_from_be_bytes(*d.data, d.start, 4u) as u32 -} -pub fn doc_as_u64(d: Doc) -> u64 { - assert d.end == d.start + 8u; - io::u64_from_be_bytes(*d.data, d.start, 8u) -} - -pub fn doc_as_i8(d: Doc) -> i8 { doc_as_u8(d) as i8 } -pub fn doc_as_i16(d: Doc) -> i16 { doc_as_u16(d) as i16 } -pub fn doc_as_i32(d: Doc) -> i32 { doc_as_u32(d) as i32 } -pub fn doc_as_i64(d: Doc) -> i64 { doc_as_u64(d) as i64 } + pub fn tagged_docs(d: Doc, tg: uint, it: fn(Doc) -> bool) { + let mut pos = d.start; + while pos < d.end { + let elt_tag = vuint_at(*d.data, pos); + let elt_size = vuint_at(*d.data, elt_tag.next); + pos = elt_size.next + elt_size.val; + if elt_tag.val == tg { + let doc = Doc { data: d.data, start: elt_size.next, end: pos }; + if !it(doc) { + break; + } + } + } + } -// ebml writing -struct Serializer { - writer: io::Writer, - priv mut size_positions: ~[uint], -} + pub fn doc_data(d: Doc) -> ~[u8] { vec::slice::<u8>(*d.data, d.start, d.end) } -fn write_sized_vuint(w: io::Writer, n: uint, size: uint) { - match size { - 1u => w.write(&[0x80u8 | (n as u8)]), - 2u => w.write(&[0x40u8 | ((n >> 8_u) as u8), n as u8]), - 3u => w.write(&[0x20u8 | ((n >> 16_u) as u8), (n >> 8_u) as u8, - n as u8]), - 4u => w.write(&[0x10u8 | ((n >> 24_u) as u8), (n >> 16_u) as u8, - (n >> 8_u) as u8, n as u8]), - _ => fail fmt!("vint to write too big: %?", n) - }; -} + pub fn with_doc_data<T>(d: Doc, f: fn(x: &[u8]) -> T) -> T { + f(vec::view(*d.data, d.start, d.end)) + } -fn write_vuint(w: io::Writer, n: uint) { - if n < 0x7f_u { write_sized_vuint(w, n, 1u); return; } - if n < 0x4000_u { write_sized_vuint(w, n, 2u); return; } - if n < 0x200000_u { write_sized_vuint(w, n, 3u); return; } - if n < 0x10000000_u { write_sized_vuint(w, n, 4u); return; } - fail fmt!("vint to write too big: %?", n); -} + pub fn doc_as_str(d: Doc) -> ~str { str::from_bytes(doc_data(d)) } -pub fn Serializer(w: io::Writer) -> Serializer { - let size_positions: ~[uint] = ~[]; - Serializer { writer: w, mut size_positions: size_positions } -} + pub fn doc_as_u8(d: Doc) -> u8 { + assert d.end == d.start + 1u; + (*d.data)[d.start] + } -// FIXME (#2741): Provide a function to write the standard ebml header. -impl Serializer { - fn start_tag(tag_id: uint) { - debug!("Start tag %u", tag_id); + pub fn doc_as_u16(d: Doc) -> u16 { + assert d.end == d.start + 2u; + io::u64_from_be_bytes(*d.data, d.start, 2u) as u16 + } - // Write the enum ID: - write_vuint(self.writer, tag_id); + pub fn doc_as_u32(d: Doc) -> u32 { + assert d.end == d.start + 4u; + io::u64_from_be_bytes(*d.data, d.start, 4u) as u32 + } - // Write a placeholder four-byte size. - self.size_positions.push(self.writer.tell()); - let zeroes: &[u8] = &[0u8, 0u8, 0u8, 0u8]; - self.writer.write(zeroes); + pub fn doc_as_u64(d: Doc) -> u64 { + assert d.end == d.start + 8u; + io::u64_from_be_bytes(*d.data, d.start, 8u) } - fn end_tag() { - let last_size_pos = self.size_positions.pop(); - let cur_pos = self.writer.tell(); - self.writer.seek(last_size_pos as int, io::SeekSet); - let size = (cur_pos - last_size_pos - 4u); - write_sized_vuint(self.writer, size, 4u); - self.writer.seek(cur_pos as int, io::SeekSet); + pub fn doc_as_i8(d: Doc) -> i8 { doc_as_u8(d) as i8 } + pub fn doc_as_i16(d: Doc) -> i16 { doc_as_u16(d) as i16 } + pub fn doc_as_i32(d: Doc) -> i32 { doc_as_u32(d) as i32 } + pub fn doc_as_i64(d: Doc) -> i64 { doc_as_u64(d) as i64 } - debug!("End tag (size = %u)", size); - } - fn wr_tag(tag_id: uint, blk: fn()) { - self.start_tag(tag_id); - blk(); - self.end_tag(); + struct Deserializer { + priv mut parent: Doc, + priv mut pos: uint, } - fn wr_tagged_bytes(tag_id: uint, b: &[u8]) { - write_vuint(self.writer, tag_id); - write_vuint(self.writer, vec::len(b)); - self.writer.write(b); + pub fn Deserializer(d: Doc) -> Deserializer { + Deserializer { mut parent: d, mut pos: d.start } } - fn wr_tagged_u64(tag_id: uint, v: u64) { - do io::u64_to_be_bytes(v, 8u) |v| { - self.wr_tagged_bytes(tag_id, v); + priv impl Deserializer { + fn _check_label(lbl: &str) { + if self.pos < self.parent.end { + let TaggedDoc { tag: r_tag, doc: r_doc } = + doc_at(self.parent.data, self.pos); + + if r_tag == (EsLabel as uint) { + self.pos = r_doc.end; + let str = doc_as_str(r_doc); + if lbl != str { + fail fmt!("Expected label %s but found %s", lbl, str); + } + } + } } - } - fn wr_tagged_u32(tag_id: uint, v: u32) { - do io::u64_to_be_bytes(v as u64, 4u) |v| { - self.wr_tagged_bytes(tag_id, v); + fn next_doc(exp_tag: EbmlSerializerTag) -> Doc { + debug!(". next_doc(exp_tag=%?)", exp_tag); + if self.pos >= self.parent.end { + fail ~"no more documents in current node!"; + } + let TaggedDoc { tag: r_tag, doc: r_doc } = + doc_at(self.parent.data, self.pos); + debug!("self.parent=%?-%? self.pos=%? r_tag=%? r_doc=%?-%?", + copy self.parent.start, copy self.parent.end, + copy self.pos, r_tag, r_doc.start, r_doc.end); + if r_tag != (exp_tag as uint) { + fail fmt!("expected EBML doc with tag %? but found tag %?", + exp_tag, r_tag); + } + if r_doc.end > self.parent.end { + fail fmt!("invalid EBML, child extends to 0x%x, parent to 0x%x", + r_doc.end, self.parent.end); + } + self.pos = r_doc.end; + r_doc } - } - fn wr_tagged_u16(tag_id: uint, v: u16) { - do io::u64_to_be_bytes(v as u64, 2u) |v| { - self.wr_tagged_bytes(tag_id, v); + fn push_doc<T>(d: Doc, f: fn() -> T) -> T{ + let old_parent = self.parent; + let old_pos = self.pos; + self.parent = d; + self.pos = d.start; + let r = f(); + self.parent = old_parent; + self.pos = old_pos; + move r } - } - fn wr_tagged_u8(tag_id: uint, v: u8) { - self.wr_tagged_bytes(tag_id, &[v]); + fn _next_uint(exp_tag: EbmlSerializerTag) -> uint { + let r = doc_as_u32(self.next_doc(exp_tag)); + debug!("_next_uint exp_tag=%? result=%?", exp_tag, r); + r as uint + } } - fn wr_tagged_i64(tag_id: uint, v: i64) { - do io::u64_to_be_bytes(v as u64, 8u) |v| { - self.wr_tagged_bytes(tag_id, v); + impl Deserializer { + fn read_opaque<R>(&self, op: fn(Doc) -> R) -> R { + do self.push_doc(self.next_doc(EsOpaque)) { + op(copy self.parent) + } } } - fn wr_tagged_i32(tag_id: uint, v: i32) { - do io::u64_to_be_bytes(v as u64, 4u) |v| { - self.wr_tagged_bytes(tag_id, v); + impl Deserializer: serialization::Deserializer { + fn read_nil(&self) -> () { () } + + fn read_u64(&self) -> u64 { doc_as_u64(self.next_doc(EsU64)) } + fn read_u32(&self) -> u32 { doc_as_u32(self.next_doc(EsU32)) } + fn read_u16(&self) -> u16 { doc_as_u16(self.next_doc(EsU16)) } + fn read_u8 (&self) -> u8 { doc_as_u8 (self.next_doc(EsU8 )) } + fn read_uint(&self) -> uint { + let v = doc_as_u64(self.next_doc(EsUint)); + if v > (core::uint::max_value as u64) { + fail fmt!("uint %? too large for this architecture", v); + } + v as uint } - } - fn wr_tagged_i16(tag_id: uint, v: i16) { - do io::u64_to_be_bytes(v as u64, 2u) |v| { - self.wr_tagged_bytes(tag_id, v); + fn read_i64(&self) -> i64 { doc_as_u64(self.next_doc(EsI64)) as i64 } + fn read_i32(&self) -> i32 { doc_as_u32(self.next_doc(EsI32)) as i32 } + fn read_i16(&self) -> i16 { doc_as_u16(self.next_doc(EsI16)) as i16 } + fn read_i8 (&self) -> i8 { doc_as_u8 (self.next_doc(EsI8 )) as i8 } + fn read_int(&self) -> int { + let v = doc_as_u64(self.next_doc(EsInt)) as i64; + if v > (int::max_value as i64) || v < (int::min_value as i64) { + fail fmt!("int %? out of range for this architecture", v); + } + v as int } - } - fn wr_tagged_i8(tag_id: uint, v: i8) { - self.wr_tagged_bytes(tag_id, &[v as u8]); - } + fn read_bool(&self) -> bool { doc_as_u8(self.next_doc(EsBool)) as bool } - fn wr_tagged_str(tag_id: uint, v: &str) { - str::byte_slice(v, |b| self.wr_tagged_bytes(tag_id, b)); - } + fn read_f64(&self) -> f64 { fail ~"read_f64()"; } + fn read_f32(&self) -> f32 { fail ~"read_f32()"; } + fn read_float(&self) -> float { fail ~"read_float()"; } - fn wr_bytes(b: &[u8]) { - debug!("Write %u bytes", vec::len(b)); - self.writer.write(b); - } + fn read_char(&self) -> char { fail ~"read_char()"; } - fn wr_str(s: &str) { - debug!("Write str: %?", s); - self.writer.write(str::to_bytes(s)); - } -} + fn read_owned_str(&self) -> ~str { doc_as_str(self.next_doc(EsStr)) } + fn read_managed_str(&self) -> @str { fail ~"read_managed_str()"; } -// FIXME (#2743): optionally perform "relaxations" on end_tag to more -// efficiently encode sizes; this is a fixed point iteration + // Compound types: + fn read_owned<T>(&self, f: fn() -> T) -> T { + debug!("read_owned()"); + f() + } -// Set to true to generate more debugging in EBML serialization. -// Totally lame approach. -const debug: bool = false; + fn read_managed<T>(&self, f: fn() -> T) -> T { + debug!("read_managed()"); + f() + } -enum EbmlSerializerTag { - EsUint, EsU64, EsU32, EsU16, EsU8, - EsInt, EsI64, EsI32, EsI16, EsI8, - EsBool, - EsStr, - EsF64, EsF32, EsFloat, - EsEnum, EsEnumVid, EsEnumBody, - EsVec, EsVecLen, EsVecElt, + fn read_enum<T>(&self, name: &str, f: fn() -> T) -> T { + debug!("read_enum(%s)", name); + self._check_label(name); + self.push_doc(self.next_doc(EsEnum), f) + } - EsOpaque, + fn read_enum_variant<T>(&self, f: fn(uint) -> T) -> T { + debug!("read_enum_variant()"); + let idx = self._next_uint(EsEnumVid); + debug!(" idx=%u", idx); + do self.push_doc(self.next_doc(EsEnumBody)) { + f(idx) + } + } - EsLabel // Used only when debugging -} + fn read_enum_variant_arg<T>(&self, idx: uint, f: fn() -> T) -> T { + debug!("read_enum_variant_arg(idx=%u)", idx); + f() + } -priv impl Serializer { - // used internally to emit things like the vector length and so on - fn _emit_tagged_uint(t: EbmlSerializerTag, v: uint) { - assert v <= 0xFFFF_FFFF_u; - self.wr_tagged_u32(t as uint, v as u32); - } + fn read_owned_vec<T>(&self, f: fn(uint) -> T) -> T { + debug!("read_owned_vec()"); + do self.push_doc(self.next_doc(EsVec)) { + let len = self._next_uint(EsVecLen); + debug!(" len=%u", len); + f(len) + } + } - fn _emit_label(label: &str) { - // There are various strings that we have access to, such as - // the name of a record field, which do not actually appear in - // the serialized EBML (normally). This is just for - // efficiency. When debugging, though, we can emit such - // labels and then they will be checked by deserializer to - // try and check failures more quickly. - if debug { self.wr_tagged_str(EsLabel as uint, label) } - } -} + fn read_managed_vec<T>(&self, f: fn(uint) -> T) -> T { + debug!("read_managed_vec()"); + do self.push_doc(self.next_doc(EsVec)) { + let len = self._next_uint(EsVecLen); + debug!(" len=%u", len); + f(len) + } + } + + fn read_vec_elt<T>(&self, idx: uint, f: fn() -> T) -> T { + debug!("read_vec_elt(idx=%u)", idx); + self.push_doc(self.next_doc(EsVecElt), f) + } -impl Serializer { - fn emit_opaque(&self, f: fn()) { - do self.wr_tag(EsOpaque as uint) { + fn read_rec<T>(&self, f: fn() -> T) -> T { + debug!("read_rec()"); f() } - } -} -impl Serializer: serialization::Serializer { - fn emit_nil(&self) {} + fn read_struct<T>(&self, name: &str, f: fn() -> T) -> T { + debug!("read_struct(name=%s)", name); + f() + } - fn emit_uint(&self, v: uint) { - self.wr_tagged_u64(EsUint as uint, v as u64); - } - fn emit_u64(&self, v: u64) { self.wr_tagged_u64(EsU64 as uint, v); } - fn emit_u32(&self, v: u32) { self.wr_tagged_u32(EsU32 as uint, v); } - fn emit_u16(&self, v: u16) { self.wr_tagged_u16(EsU16 as uint, v); } - fn emit_u8(&self, v: u8) { self.wr_tagged_u8 (EsU8 as uint, v); } + fn read_field<T>(&self, name: &str, idx: uint, f: fn() -> T) -> T { + debug!("read_field(name=%s, idx=%u)", name, idx); + self._check_label(name); + f() + } - fn emit_int(&self, v: int) { - self.wr_tagged_i64(EsInt as uint, v as i64); - } - fn emit_i64(&self, v: i64) { self.wr_tagged_i64(EsI64 as uint, v); } - fn emit_i32(&self, v: i32) { self.wr_tagged_i32(EsI32 as uint, v); } - fn emit_i16(&self, v: i16) { self.wr_tagged_i16(EsI16 as uint, v); } - fn emit_i8(&self, v: i8) { self.wr_tagged_i8 (EsI8 as uint, v); } + fn read_tup<T>(&self, len: uint, f: fn() -> T) -> T { + debug!("read_tup(len=%u)", len); + f() + } - fn emit_bool(&self, v: bool) { - self.wr_tagged_u8(EsBool as uint, v as u8) + fn read_tup_elt<T>(&self, idx: uint, f: fn() -> T) -> T { + debug!("read_tup_elt(idx=%u)", idx); + f() + } } - // FIXME (#2742): implement these - fn emit_f64(&self, _v: f64) { fail ~"Unimplemented: serializing an f64"; } - fn emit_f32(&self, _v: f32) { fail ~"Unimplemented: serializing an f32"; } - fn emit_float(&self, _v: float) { - fail ~"Unimplemented: serializing a float"; - } +} - fn emit_char(&self, _v: char) { - fail ~"Unimplemented: serializing a char"; +pub mod Writer { + + // ebml writing + struct Serializer { + writer: io::Writer, + priv mut size_positions: ~[uint], } - fn emit_borrowed_str(&self, v: &str) { - self.wr_tagged_str(EsStr as uint, v) + fn write_sized_vuint(w: io::Writer, n: uint, size: uint) { + match size { + 1u => w.write(&[0x80u8 | (n as u8)]), + 2u => w.write(&[0x40u8 | ((n >> 8_u) as u8), n as u8]), + 3u => w.write(&[0x20u8 | ((n >> 16_u) as u8), (n >> 8_u) as u8, + n as u8]), + 4u => w.write(&[0x10u8 | ((n >> 24_u) as u8), (n >> 16_u) as u8, + (n >> 8_u) as u8, n as u8]), + _ => fail fmt!("vint to write too big: %?", n) + }; } - fn emit_owned_str(&self, v: &str) { - self.emit_borrowed_str(v) + fn write_vuint(w: io::Writer, n: uint) { + if n < 0x7f_u { write_sized_vuint(w, n, 1u); return; } + if n < 0x4000_u { write_sized_vuint(w, n, 2u); return; } + if n < 0x200000_u { write_sized_vuint(w, n, 3u); return; } + if n < 0x10000000_u { write_sized_vuint(w, n, 4u); return; } + fail fmt!("vint to write too big: %?", n); } - fn emit_managed_str(&self, v: &str) { - self.emit_borrowed_str(v) + pub fn Serializer(w: io::Writer) -> Serializer { + let size_positions: ~[uint] = ~[]; + Serializer { writer: w, mut size_positions: size_positions } } - fn emit_borrowed(&self, f: fn()) { f() } - fn emit_owned(&self, f: fn()) { f() } - fn emit_managed(&self, f: fn()) { f() } + // FIXME (#2741): Provide a function to write the standard ebml header. + impl Serializer { + fn start_tag(tag_id: uint) { + debug!("Start tag %u", tag_id); - fn emit_enum(&self, name: &str, f: fn()) { - self._emit_label(name); - self.wr_tag(EsEnum as uint, f) - } - fn emit_enum_variant(&self, _v_name: &str, v_id: uint, _cnt: uint, - f: fn()) { - self._emit_tagged_uint(EsEnumVid, v_id); - self.wr_tag(EsEnumBody as uint, f) - } - fn emit_enum_variant_arg(&self, _idx: uint, f: fn()) { f() } + // Write the enum ID: + write_vuint(self.writer, tag_id); - fn emit_borrowed_vec(&self, len: uint, f: fn()) { - do self.wr_tag(EsVec as uint) { - self._emit_tagged_uint(EsVecLen, len); - f() + // Write a placeholder four-byte size. + self.size_positions.push(self.writer.tell()); + let zeroes: &[u8] = &[0u8, 0u8, 0u8, 0u8]; + self.writer.write(zeroes); } - } - fn emit_owned_vec(&self, len: uint, f: fn()) { - self.emit_borrowed_vec(len, f) - } + fn end_tag() { + let last_size_pos = self.size_positions.pop(); + let cur_pos = self.writer.tell(); + self.writer.seek(last_size_pos as int, io::SeekSet); + let size = (cur_pos - last_size_pos - 4u); + write_sized_vuint(self.writer, size, 4u); + self.writer.seek(cur_pos as int, io::SeekSet); - fn emit_managed_vec(&self, len: uint, f: fn()) { - self.emit_borrowed_vec(len, f) - } + debug!("End tag (size = %u)", size); + } - fn emit_vec_elt(&self, _idx: uint, f: fn()) { - self.wr_tag(EsVecElt as uint, f) - } + fn wr_tag(tag_id: uint, blk: fn()) { + self.start_tag(tag_id); + blk(); + self.end_tag(); + } - fn emit_rec(&self, f: fn()) { f() } - fn emit_struct(&self, _name: &str, f: fn()) { f() } - fn emit_field(&self, name: &str, _idx: uint, f: fn()) { - self._emit_label(name); - f() - } + fn wr_tagged_bytes(tag_id: uint, b: &[u8]) { + write_vuint(self.writer, tag_id); + write_vuint(self.writer, vec::len(b)); + self.writer.write(b); + } - fn emit_tup(&self, _len: uint, f: fn()) { f() } - fn emit_tup_elt(&self, _idx: uint, f: fn()) { f() } -} + fn wr_tagged_u64(tag_id: uint, v: u64) { + do io::u64_to_be_bytes(v, 8u) |v| { + self.wr_tagged_bytes(tag_id, v); + } + } -struct Deserializer { - priv mut parent: Doc, - priv mut pos: uint, -} + fn wr_tagged_u32(tag_id: uint, v: u32) { + do io::u64_to_be_bytes(v as u64, 4u) |v| { + self.wr_tagged_bytes(tag_id, v); + } + } -pub fn Deserializer(d: Doc) -> Deserializer { - Deserializer { mut parent: d, mut pos: d.start } -} + fn wr_tagged_u16(tag_id: uint, v: u16) { + do io::u64_to_be_bytes(v as u64, 2u) |v| { + self.wr_tagged_bytes(tag_id, v); + } + } -priv impl Deserializer { - fn _check_label(lbl: &str) { - if self.pos < self.parent.end { - let TaggedDoc { tag: r_tag, doc: r_doc } = - doc_at(self.parent.data, self.pos); + fn wr_tagged_u8(tag_id: uint, v: u8) { + self.wr_tagged_bytes(tag_id, &[v]); + } - if r_tag == (EsLabel as uint) { - self.pos = r_doc.end; - let str = doc_as_str(r_doc); - if lbl != str { - fail fmt!("Expected label %s but found %s", lbl, str); - } + fn wr_tagged_i64(tag_id: uint, v: i64) { + do io::u64_to_be_bytes(v as u64, 8u) |v| { + self.wr_tagged_bytes(tag_id, v); } } - } - fn next_doc(exp_tag: EbmlSerializerTag) -> Doc { - debug!(". next_doc(exp_tag=%?)", exp_tag); - if self.pos >= self.parent.end { - fail ~"no more documents in current node!"; - } - let TaggedDoc { tag: r_tag, doc: r_doc } = - doc_at(self.parent.data, self.pos); - debug!("self.parent=%?-%? self.pos=%? r_tag=%? r_doc=%?-%?", - copy self.parent.start, copy self.parent.end, - copy self.pos, r_tag, r_doc.start, r_doc.end); - if r_tag != (exp_tag as uint) { - fail fmt!("expected EMBL doc with tag %? but found tag %?", - exp_tag, r_tag); - } - if r_doc.end > self.parent.end { - fail fmt!("invalid EBML, child extends to 0x%x, parent to 0x%x", - r_doc.end, self.parent.end); - } - self.pos = r_doc.end; - r_doc - } + fn wr_tagged_i32(tag_id: uint, v: i32) { + do io::u64_to_be_bytes(v as u64, 4u) |v| { + self.wr_tagged_bytes(tag_id, v); + } + } - fn push_doc<T>(d: Doc, f: fn() -> T) -> T{ - let old_parent = self.parent; - let old_pos = self.pos; - self.parent = d; - self.pos = d.start; - let r = f(); - self.parent = old_parent; - self.pos = old_pos; - move r - } + fn wr_tagged_i16(tag_id: uint, v: i16) { + do io::u64_to_be_bytes(v as u64, 2u) |v| { + self.wr_tagged_bytes(tag_id, v); + } + } - fn _next_uint(exp_tag: EbmlSerializerTag) -> uint { - let r = doc_as_u32(self.next_doc(exp_tag)); - debug!("_next_uint exp_tag=%? result=%?", exp_tag, r); - r as uint - } -} + fn wr_tagged_i8(tag_id: uint, v: i8) { + self.wr_tagged_bytes(tag_id, &[v as u8]); + } + + fn wr_tagged_str(tag_id: uint, v: &str) { + str::byte_slice(v, |b| self.wr_tagged_bytes(tag_id, b)); + } + + fn wr_bytes(b: &[u8]) { + debug!("Write %u bytes", vec::len(b)); + self.writer.write(b); + } -impl Deserializer { - fn read_opaque<R>(&self, op: fn(Doc) -> R) -> R { - do self.push_doc(self.next_doc(EsOpaque)) { - op(copy self.parent) + fn wr_str(s: &str) { + debug!("Write str: %?", s); + self.writer.write(str::to_bytes(s)); } } -} -impl Deserializer: serialization::Deserializer { - fn read_nil(&self) -> () { () } + // FIXME (#2743): optionally perform "relaxations" on end_tag to more + // efficiently encode sizes; this is a fixed point iteration + + // Set to true to generate more debugging in EBML serialization. + // Totally lame approach. + const debug: bool = false; + + priv impl Serializer { + // used internally to emit things like the vector length and so on + fn _emit_tagged_uint(t: EbmlSerializerTag, v: uint) { + assert v <= 0xFFFF_FFFF_u; + self.wr_tagged_u32(t as uint, v as u32); + } - fn read_u64(&self) -> u64 { doc_as_u64(self.next_doc(EsU64)) } - fn read_u32(&self) -> u32 { doc_as_u32(self.next_doc(EsU32)) } - fn read_u16(&self) -> u16 { doc_as_u16(self.next_doc(EsU16)) } - fn read_u8 (&self) -> u8 { doc_as_u8 (self.next_doc(EsU8 )) } - fn read_uint(&self) -> uint { - let v = doc_as_u64(self.next_doc(EsUint)); - if v > (core::uint::max_value as u64) { - fail fmt!("uint %? too large for this architecture", v); + fn _emit_label(label: &str) { + // There are various strings that we have access to, such as + // the name of a record field, which do not actually appear in + // the serialized EBML (normally). This is just for + // efficiency. When debugging, though, we can emit such + // labels and then they will be checked by deserializer to + // try and check failures more quickly. + if debug { self.wr_tagged_str(EsLabel as uint, label) } } - v as uint } - fn read_i64(&self) -> i64 { doc_as_u64(self.next_doc(EsI64)) as i64 } - fn read_i32(&self) -> i32 { doc_as_u32(self.next_doc(EsI32)) as i32 } - fn read_i16(&self) -> i16 { doc_as_u16(self.next_doc(EsI16)) as i16 } - fn read_i8 (&self) -> i8 { doc_as_u8 (self.next_doc(EsI8 )) as i8 } - fn read_int(&self) -> int { - let v = doc_as_u64(self.next_doc(EsInt)) as i64; - if v > (int::max_value as i64) || v < (int::min_value as i64) { - fail fmt!("int %? out of range for this architecture", v); + impl Serializer { + fn emit_opaque(&self, f: fn()) { + do self.wr_tag(EsOpaque as uint) { + f() + } } - v as int } - fn read_bool(&self) -> bool { doc_as_u8(self.next_doc(EsBool)) as bool } + impl Serializer: serialization::Serializer { + fn emit_nil(&self) {} - fn read_f64(&self) -> f64 { fail ~"read_f64()"; } - fn read_f32(&self) -> f32 { fail ~"read_f32()"; } - fn read_float(&self) -> float { fail ~"read_float()"; } + fn emit_uint(&self, v: uint) { + self.wr_tagged_u64(EsUint as uint, v as u64); + } + fn emit_u64(&self, v: u64) { self.wr_tagged_u64(EsU64 as uint, v); } + fn emit_u32(&self, v: u32) { self.wr_tagged_u32(EsU32 as uint, v); } + fn emit_u16(&self, v: u16) { self.wr_tagged_u16(EsU16 as uint, v); } + fn emit_u8(&self, v: u8) { self.wr_tagged_u8 (EsU8 as uint, v); } - fn read_char(&self) -> char { fail ~"read_char()"; } + fn emit_int(&self, v: int) { + self.wr_tagged_i64(EsInt as uint, v as i64); + } + fn emit_i64(&self, v: i64) { self.wr_tagged_i64(EsI64 as uint, v); } + fn emit_i32(&self, v: i32) { self.wr_tagged_i32(EsI32 as uint, v); } + fn emit_i16(&self, v: i16) { self.wr_tagged_i16(EsI16 as uint, v); } + fn emit_i8(&self, v: i8) { self.wr_tagged_i8 (EsI8 as uint, v); } - fn read_owned_str(&self) -> ~str { doc_as_str(self.next_doc(EsStr)) } - fn read_managed_str(&self) -> @str { fail ~"read_managed_str()"; } + fn emit_bool(&self, v: bool) { + self.wr_tagged_u8(EsBool as uint, v as u8) + } - // Compound types: - fn read_owned<T>(&self, f: fn() -> T) -> T { - debug!("read_owned()"); - f() - } + // FIXME (#2742): implement these + fn emit_f64(&self, _v: f64) { fail ~"Unimplemented: serializing an f64"; } + fn emit_f32(&self, _v: f32) { fail ~"Unimplemented: serializing an f32"; } + fn emit_float(&self, _v: float) { + fail ~"Unimplemented: serializing a float"; + } - fn read_managed<T>(&self, f: fn() -> T) -> T { - debug!("read_managed()"); - f() - } + fn emit_char(&self, _v: char) { + fail ~"Unimplemented: serializing a char"; + } - fn read_enum<T>(&self, name: &str, f: fn() -> T) -> T { - debug!("read_enum(%s)", name); - self._check_label(name); - self.push_doc(self.next_doc(EsEnum), f) - } + fn emit_borrowed_str(&self, v: &str) { + self.wr_tagged_str(EsStr as uint, v) + } - fn read_enum_variant<T>(&self, f: fn(uint) -> T) -> T { - debug!("read_enum_variant()"); - let idx = self._next_uint(EsEnumVid); - debug!(" idx=%u", idx); - do self.push_doc(self.next_doc(EsEnumBody)) { - f(idx) + fn emit_owned_str(&self, v: &str) { + self.emit_borrowed_str(v) } - } - fn read_enum_variant_arg<T>(&self, idx: uint, f: fn() -> T) -> T { - debug!("read_enum_variant_arg(idx=%u)", idx); - f() - } + fn emit_managed_str(&self, v: &str) { + self.emit_borrowed_str(v) + } + + fn emit_borrowed(&self, f: fn()) { f() } + fn emit_owned(&self, f: fn()) { f() } + fn emit_managed(&self, f: fn()) { f() } - fn read_owned_vec<T>(&self, f: fn(uint) -> T) -> T { - debug!("read_owned_vec()"); - do self.push_doc(self.next_doc(EsVec)) { - let len = self._next_uint(EsVecLen); - debug!(" len=%u", len); - f(len) + fn emit_enum(&self, name: &str, f: fn()) { + self._emit_label(name); + self.wr_tag(EsEnum as uint, f) } - } + fn emit_enum_variant(&self, _v_name: &str, v_id: uint, _cnt: uint, + f: fn()) { + self._emit_tagged_uint(EsEnumVid, v_id); + self.wr_tag(EsEnumBody as uint, f) + } + fn emit_enum_variant_arg(&self, _idx: uint, f: fn()) { f() } - fn read_managed_vec<T>(&self, f: fn(uint) -> T) -> T { - debug!("read_managed_vec()"); - do self.push_doc(self.next_doc(EsVec)) { - let len = self._next_uint(EsVecLen); - debug!(" len=%u", len); - f(len) + fn emit_borrowed_vec(&self, len: uint, f: fn()) { + do self.wr_tag(EsVec as uint) { + self._emit_tagged_uint(EsVecLen, len); + f() + } } - } - fn read_vec_elt<T>(&self, idx: uint, f: fn() -> T) -> T { - debug!("read_vec_elt(idx=%u)", idx); - self.push_doc(self.next_doc(EsVecElt), f) - } + fn emit_owned_vec(&self, len: uint, f: fn()) { + self.emit_borrowed_vec(len, f) + } - fn read_rec<T>(&self, f: fn() -> T) -> T { - debug!("read_rec()"); - f() - } + fn emit_managed_vec(&self, len: uint, f: fn()) { + self.emit_borrowed_vec(len, f) + } - fn read_struct<T>(&self, name: &str, f: fn() -> T) -> T { - debug!("read_struct(name=%s)", name); - f() - } + fn emit_vec_elt(&self, _idx: uint, f: fn()) { + self.wr_tag(EsVecElt as uint, f) + } - fn read_field<T>(&self, name: &str, idx: uint, f: fn() -> T) -> T { - debug!("read_field(name=%s, idx=%u)", name, idx); - self._check_label(name); - f() - } + fn emit_rec(&self, f: fn()) { f() } + fn emit_struct(&self, _name: &str, f: fn()) { f() } + fn emit_field(&self, name: &str, _idx: uint, f: fn()) { + self._emit_label(name); + f() + } - fn read_tup<T>(&self, len: uint, f: fn() -> T) -> T { - debug!("read_tup(len=%u)", len); - f() + fn emit_tup(&self, _len: uint, f: fn()) { f() } + fn emit_tup_elt(&self, _idx: uint, f: fn()) { f() } } - fn read_tup_elt<T>(&self, idx: uint, f: fn() -> T) -> T { - debug!("read_tup_elt(idx=%u)", idx); - f() - } } - // ___________________________________________________________________________ // Testing @@ -627,11 +635,11 @@ mod tests { fn test_v(v: Option<int>) { debug!("v == %?", v); let bytes = do io::with_bytes_writer |wr| { - let ebml_w = Serializer(wr); + let ebml_w = Writer::Serializer(wr); v.serialize(&ebml_w) }; - let ebml_doc = Doc(@bytes); - let deser = Deserializer(ebml_doc); + let ebml_doc = Reader::Doc(@bytes); + let deser = Reader::Deserializer(ebml_doc); let v1 = serialization::deserialize(&deser); debug!("v1 == %?", v1); assert v == v1; |
