aboutsummaryrefslogtreecommitdiffstats
path: root/vespajlib/src/main/java/com/yahoo/slime/BinaryDecoder.java
blob: e2cbed2e880081c44ba07e3178f1ee0fdc434cb2 (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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.slime;


import static com.yahoo.slime.BinaryFormat.decode_double;
import static com.yahoo.slime.BinaryFormat.decode_meta;
import static com.yahoo.slime.BinaryFormat.decode_type;
import static com.yahoo.slime.BinaryFormat.decode_zigzag;

final class BinaryDecoder {

    BufferedInput in;

    private final SlimeInserter slimeInserter = new SlimeInserter(null);
    private final ArrayInserter arrayInserter = new ArrayInserter(null);
    private final ObjectSymbolInserter objectInserter = new ObjectSymbolInserter(null, 0);

    public BinaryDecoder() {}

    public Slime decode(byte[] bytes) {
        return decode(bytes, 0, bytes.length);
    }
    public Slime decode(byte[] bytes, int offset, int length) {
        Slime slime = new Slime();
        in = new BufferedInput(bytes, offset, length);
        decodeSymbolTable(in, slime.symbolTable());
        decodeValue(slimeInserter.adjust(slime));
        if (in.failed()) {
            slime.wrap("partial_result");
            slime.get().setData("offending_input", in.getOffending());
            slime.get().setString("error_message", in.getErrorMessage());
        }
        return slime;
    }

    long read_bytes_le(int bytes) {
        long value = 0;
        int shift = 0;
        for (int i = 0; i < bytes; ++i) {
            long b = in.getByte();
            value |= (b & 0xff) << shift;
            shift += 8;
        }
        return value;
    }

    long read_bytes_be(int bytes) {
        long value = 0;
        int shift = 56;
        for (int i = 0; i < bytes; ++i) {
            long b = in.getByte();
            value |= (b & 0xff) << shift;
            shift -= 8;
        }
        return value;
    }

    Cursor decodeNIX(Inserter inserter) {
        return inserter.insertNIX();
    }

    Cursor decodeBOOL(Inserter inserter, int meta) {
        return inserter.insertBOOL(meta != 0);
    }

    Cursor decodeLONG(Inserter inserter, int meta) {
        long encoded = read_bytes_le(meta);
        return inserter.insertLONG(decode_zigzag(encoded));
    }

    Cursor decodeDOUBLE(Inserter inserter, int meta) {
        long encoded = read_bytes_be(meta);
        return inserter.insertDOUBLE(decode_double(encoded));
    }

    Cursor decodeSTRING(Inserter inserter, int meta) {
        int size = in.read_size(meta);
        byte[] image = in.getBytes(size);
        return inserter.insertSTRING(image);
    }

    Cursor decodeDATA(Inserter inserter, int meta) {
        int size = in.read_size(meta);
        byte[] image = in.getBytes(size);
        return inserter.insertDATA(image);
    }

    Cursor decodeARRAY(Inserter inserter, int meta) {
        Cursor cursor = inserter.insertARRAY();
        int size = in.read_size(meta);
        for (int i = 0; i < size; ++i) {
            decodeValue(arrayInserter.adjust(cursor));
        }
        return cursor;
    }

    Cursor decodeOBJECT(Inserter inserter, int meta) {
        Cursor cursor = inserter.insertOBJECT();
        int size = in.read_size(meta);
        for (int i = 0; i < size; ++i) {
            int symbol = in.read_cmpr_int();
            decodeValue(objectInserter.adjust(cursor, symbol));
        }
        return cursor;
    }

    Cursor decodeValue(Inserter inserter, Type type, int meta) {
        switch (type) {
        case NIX:     return decodeNIX(inserter);
        case BOOL:    return decodeBOOL(inserter, meta);
        case LONG:    return decodeLONG(inserter, meta);
        case DOUBLE:  return decodeDOUBLE(inserter, meta);
        case STRING:  return decodeSTRING(inserter, meta);
        case DATA:    return decodeDATA(inserter, meta);
        case ARRAY:   return decodeARRAY(inserter, meta);
        case OBJECT:  return decodeOBJECT(inserter, meta);
        }
        assert false : "should not be reached";
        return null;
    }

    void decodeValue(Inserter inserter) {
        byte b = in.getByte();
        Cursor cursor = decodeValue(inserter, decode_type(b), decode_meta(b));
        if (!cursor.valid()) {
            in.fail("failed to decode value");
        }
    }

    static void decodeSymbolTable(BufferedInput input, SymbolTable names) {
        int numSymbols = input.read_cmpr_int();
        final byte[] backing = input.getBacking();
        for (int i = 0; i < numSymbols; ++i) {
            int size = input.read_cmpr_int();
            int offset = input.getPosition();
            input.skip(size);
            int symbol = names.insert(Utf8Codec.decode(backing, offset, size));
            if (symbol != i) {
                input.fail("duplicate symbols in symbol table");
                return;
            }
        }
    }
}