aboutsummaryrefslogtreecommitdiff
path: root/src/lib/ebml.rs
blob: 7b413c635f219e346b04844263644e69f1ed8f15 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Simple Extensible Binary Markup Language (EBML) reader and writer on a
// cursor model. See the specification here:
//     http://www.matroska.org/technical/specs/rfc/index.html

import option.none;
import option.some;

type ebml_tag = rec(uint id, uint size);
type ebml_state = rec(ebml_tag ebml_tag, uint pos);

// TODO: When we have module renaming, make "reader" and "writer" separate
// modules within this file.

// EBML reading

type reader = rec(
    io.reader reader,
    mutable vec[ebml_state] states,
    uint size
);

// TODO: eventually use u64 or big here
impure fn read_vint(&io.reader reader) -> uint {
    auto a = reader.read_byte();
    if (a & 0x80u8 != 0u8) { ret (a & 0x7fu8) as uint; }
    auto b = reader.read_byte();
    if (a & 0x40u8 != 0u8) {
        ret (((a & 0x3fu8) as uint) << 8u) | (b as uint);
    }
    auto c = reader.read_byte();
    if (a & 0x20u8 != 0u8) {
        ret (((a & 0x1fu8) as uint) << 16u) | ((b as uint) << 8u) |
            (c as uint);
    }
    auto d = reader.read_byte();
    if (a & 0x10u8 != 0u8) {
        ret (((a & 0x0fu8) as uint) << 24u) | ((b as uint) << 16u) |
            ((c as uint) << 8u) | (d as uint);
    }

    log "vint too big"; fail;
}

impure fn create_reader(&io.reader r) -> reader {
    let vec[ebml_state] states = vec();

    // Calculate the size of the stream.
    auto pos = r.tell();
    r.seek(0, io.seek_end);
    auto size = r.tell() - pos;
    r.seek(pos as int, io.seek_set);

    ret rec(reader=r, mutable states=states, size=size);
}

impure fn bytes_left(&reader r) -> uint {
    auto pos = r.reader.tell();
    alt (_vec.last[ebml_state](r.states)) {
        case (none[ebml_state])      { ret r.size - pos; }
        case (some[ebml_state](?st)) { ret st.pos + st.ebml_tag.size - pos; }
    }
}

impure fn read_tag(&reader r) -> ebml_tag {
    auto id = read_vint(r.reader);
    auto size = read_vint(r.reader);
    ret rec(id=id, size=size);
}

// Reads a tag and moves the cursor to its first child or data segment.
impure fn move_to_first_child(&reader r) {
    auto pos = r.reader.tell();
    auto t = read_tag(r);
    r.states += vec(rec(ebml_tag=t, pos=pos));
}

// Reads a tag and skips over its contents, moving to its next sibling.
impure fn move_to_next_sibling(&reader r) {
    auto t = read_tag(r);
    r.reader.seek(t.size as int, io.seek_cur);
}

// Moves to the parent of this tag.
impure fn move_to_parent(&reader r) {
    check (_vec.len[ebml_state](r.states) > 0u);
    auto st = _vec.pop[ebml_state](r.states);
    r.reader.seek(st.pos as int, io.seek_set);
}

// Reads the data segment of a tag.
impure fn read_data(&reader r) -> vec[u8] {
    ret r.reader.read_bytes(bytes_left(r));
}

impure fn peek(&reader r) -> ebml_tag {
    check (bytes_left(r) > 0u);
    auto pos = r.reader.tell();
    auto t = read_tag(r);
    r.reader.seek(pos as int, io.seek_set);
    ret t;
}


// EBML writing

type writer = rec(io.writer writer, mutable vec[ebml_state] states);

// TODO