aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/decode.go
blob: d93c1b9dbfd7cea5fe7b86520548181d3729fa94 (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
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package snappy

import (
    "encoding/binary"
    "errors"
)

// ErrCorrupt reports that the input is invalid.
var ErrCorrupt = errors.New("snappy: corrupt input")

// DecodedLen returns the length of the decoded block.
func DecodedLen(src []byte) (int, error) {
    v, _, err := decodedLen(src)
    return v, err
}

// decodedLen returns the length of the decoded block and the number of bytes
// that the length header occupied.
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
    v, n := binary.Uvarint(src)
    if n == 0 {
        return 0, 0, ErrCorrupt
    }
    if uint64(int(v)) != v {
        return 0, 0, errors.New("snappy: decoded block is too large")
    }
    return int(v), n, nil
}

// Decode returns the decoded form of src. The returned slice may be a sub-
// slice of dst if dst was large enough to hold the entire decoded block.
// Otherwise, a newly allocated slice will be returned.
// It is valid to pass a nil dst.
func Decode(dst, src []byte) ([]byte, error) {
    dLen, s, err := decodedLen(src)
    if err != nil {
        return nil, err
    }
    if len(dst) < dLen {
        dst = make([]byte, dLen)
    }

    var d, offset, length int
    for s < len(src) {
        switch src[s] & 0x03 {
        case tagLiteral:
            x := uint(src[s] >> 2)
            switch {
            case x < 60:
                s += 1
            case x == 60:
                s += 2
                if s > len(src) {
                    return nil, ErrCorrupt
                }
                x = uint(src[s-1])
            case x == 61:
                s += 3
                if s > len(src) {
                    return nil, ErrCorrupt
                }
                x = uint(src[s-2]) | uint(src[s-1])<<8
            case x == 62:
                s += 4
                if s > len(src) {
                    return nil, ErrCorrupt
                }
                x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16
            case x == 63:
                s += 5
                if s > len(src) {
                    return nil, ErrCorrupt
                }
                x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24
            }
            length = int(x + 1)
            if length <= 0 {
                return nil, errors.New("snappy: unsupported literal length")
            }
            if length > len(dst)-d || length > len(src)-s {
                return nil, ErrCorrupt
            }
            copy(dst[d:], src[s:s+length])
            d += length
            s += length
            continue

        case tagCopy1:
            s += 2
            if s > len(src) {
                return nil, ErrCorrupt
            }
            length = 4 + int(src[s-2])>>2&0x7
            offset = int(src[s-2])&0xe0<<3 | int(src[s-1])

        case tagCopy2:
            s += 3
            if s > len(src) {
                return nil, ErrCorrupt
            }
            length = 1 + int(src[s-3])>>2
            offset = int(src[s-2]) | int(src[s-1])<<8

        case tagCopy4:
            return nil, errors.New("snappy: unsupported COPY_4 tag")
        }

        end := d + length
        if offset > d || end > len(dst) {
            return nil, ErrCorrupt
        }
        for ; d < end; d++ {
            dst[d] = dst[d-offset]
        }
    }
    if d != dLen {
        return nil, ErrCorrupt
    }
    return dst[:d], nil
}