summaryrefslogtreecommitdiffstats
path: root/vespajlib/src/main/java/com/yahoo/slime/BinaryDecoder.java
blob: 13e08484d7c2cfea990a6efc6ea7074292c722c7 (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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Copyright 2017 Yahoo Holdings. 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(slime);
        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_cmpr_long() {
        long next = in.getByte();
        long value = (next & 0x7f);
        int shift = 7;
        while ((next & 0x80) != 0) {
            next = in.getByte();
            value |= ((next & 0x7f) << shift);
            shift += 7;
        }
        return value;
    }

    long read_size(int meta) {
        return (meta == 0) ? read_cmpr_long() : (meta - 1);
    }

    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) {
        long size = read_size(meta);
        int sz = (int)size; // XXX
        byte[] image = in.getBytes(sz);
        return inserter.insertSTRING(image);
    }

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

    Cursor decodeARRAY(Inserter inserter, int meta) {
        Cursor cursor = inserter.insertARRAY();
        long size = 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();
        long size = read_size(meta);
        for (int i = 0; i < size; ++i) {
            long l = read_cmpr_long();
            int symbol = (int)l; // check for overflow?
            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");
        }
    }

    void decodeSymbolTable(Slime slime) {
        long numSymbols = read_cmpr_long();
        final byte [] backing = in.getBacking();
        for (int i = 0; i < numSymbols; ++i) {
            long size = read_cmpr_long();
            int sz = (int)size; // XXX
            int offset = in.getPosition();
            in.skip(sz);
            int symbol = slime.insert(Utf8Codec.decode(backing, offset, sz));
            if (symbol != i) {
                in.fail("duplicate symbols in symbol table");
                return;
            }
        }
    }
}