aboutsummaryrefslogtreecommitdiff
path: root/shared/sfnt/directory/directory.zig
blob: e6c5ba774fa1d51d110a184ecf85f6566011610e (plain)
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
//! SFNT Table Directory

const sfnt = @import("shared").sfnt;

const constants = sfnt.constants;

const ParseError = sfnt.errors.parse.ParseError;
const Reader = sfnt.types.reader.Reader;
const TableRecord = sfnt.types.record.TableRecord;

const limits = constants.limits;
const tags = constants.tags;

pub const Directory = struct {
    Data: []const u8,
    Records: [limits.MAX_TABLES]TableRecord,
    Count: u16,

    pub fn parse(data: []const u8) ParseError!Directory {
        var reader = Reader.initialize(data);

        const version = try reader.readU32();
        if (version == tags.VERSION_COLLECTION) {
            return ParseError.UnsupportedCollection;
        }
        if (version == tags.VERSION_OPENTYPE) {
            return ParseError.UnsupportedOutlines;
        }
        if (version != tags.VERSION_TRUETYPE and version != tags.VERSION_TRUE) {
            return ParseError.InvalidSignature;
        }

        const count = try reader.readU16();
        if (count > limits.MAX_TABLES) {
            return ParseError.TooManyTables;
        }
        try reader.skip(6);

        var directory = Directory{
            .Data = data,
            .Records = undefined,
            .Count = count,
        };

        var index: u16 = 0;
        while (index < count) : (index += 1) {
            const tag = try reader.readU32();
            _ = try reader.readU32();
            const offset = try reader.readU32();
            const length = try reader.readU32();

            if (offset > data.len or length > data.len - offset) {
                return ParseError.TableOutOfBounds;
            }

            directory.Records[index] = TableRecord{
                .Tag = tag,
                .Offset = offset,
                .Length = length,
            };
        }

        return directory;
    }

    pub fn find(self: *const Directory, tag: u32) ?[]const u8 {
        var index: u16 = 0;
        while (index < self.Count) : (index += 1) {
            const entry = self.Records[index];
            if (entry.Tag == tag) {
                return self.Data[entry.Offset .. entry.Offset + entry.Length];
            }
        }
        return null;
    }

    pub fn require(self: *const Directory, tag: u32) ParseError![]const u8 {
        return self.find(tag) orelse ParseError.TableMissing;
    }
};