1 /*
2  * Snappy decompression algorithm
3  * Copyright (c) 2015 Luca Barbato
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include "libavutil/mem.h"
23 
24 #include "bytestream.h"
25 #include "snappy.h"
26 
27 enum {
28     SNAPPY_LITERAL,
29     SNAPPY_COPY_1,
30     SNAPPY_COPY_2,
31     SNAPPY_COPY_4,
32 };
33 
bytestream2_get_levarint(GetByteContext * gb)34 static int64_t bytestream2_get_levarint(GetByteContext *gb)
35 {
36     uint64_t val = 0;
37     int shift = 0;
38     int tmp;
39 
40     do {
41         tmp = bytestream2_get_byte(gb);
42         if (shift > 31 || ((tmp & 127LL) << shift) > INT_MAX)
43             return AVERROR_INVALIDDATA;
44         val |= (tmp & 127) << shift;
45         shift += 7;
46     } while (tmp & 128);
47 
48     return val;
49 }
50 
snappy_literal(GetByteContext * gb,uint8_t * p,int size,int val)51 static int snappy_literal(GetByteContext *gb, uint8_t *p, int size, int val)
52 {
53     unsigned int len = 1;
54 
55     switch (val) {
56     case 63:
57         len += bytestream2_get_le32(gb);
58         break;
59     case 62:
60         len += bytestream2_get_le24(gb);
61         break;
62     case 61:
63         len += bytestream2_get_le16(gb);
64         break;
65     case 60:
66         len += bytestream2_get_byte(gb);
67         break;
68     default: // val < 60
69         len += val;
70     }
71 
72     if (size < len)
73         return AVERROR_INVALIDDATA;
74 
75     bytestream2_get_buffer(gb, p, len);
76 
77     return len;
78 }
79 
snappy_copy(uint8_t * start,uint8_t * p,int size,unsigned int off,int len)80 static int snappy_copy(uint8_t *start, uint8_t *p, int size,
81                        unsigned int off, int len)
82 {
83     uint8_t *q;
84     int i;
85     if (off > p - start || size < len)
86         return AVERROR_INVALIDDATA;
87 
88     q = p - off;
89 
90     for (i = 0; i < len; i++)
91         p[i] = q[i];
92 
93     return len;
94 }
95 
snappy_copy1(GetByteContext * gb,uint8_t * start,uint8_t * p,int size,int val)96 static int snappy_copy1(GetByteContext *gb, uint8_t *start, uint8_t *p,
97                         int size, int val)
98 {
99     int len          = 4 + (val & 0x7);
100     unsigned int off = bytestream2_get_byte(gb) | (val & 0x38) << 5;
101 
102     return snappy_copy(start, p, size, off, len);
103 }
104 
snappy_copy2(GetByteContext * gb,uint8_t * start,uint8_t * p,int size,int val)105 static int snappy_copy2(GetByteContext *gb, uint8_t *start, uint8_t *p,
106                         int size, int val)
107 {
108     int len          = 1 + val;
109     unsigned int off = bytestream2_get_le16(gb);
110 
111     return snappy_copy(start, p, size, off, len);
112 }
113 
snappy_copy4(GetByteContext * gb,uint8_t * start,uint8_t * p,int size,int val)114 static int snappy_copy4(GetByteContext *gb, uint8_t *start, uint8_t *p,
115                         int size, int val)
116 {
117     int len          = 1 + val;
118     unsigned int off = bytestream2_get_le32(gb);
119 
120     return snappy_copy(start, p, size, off, len);
121 }
122 
decode_len(GetByteContext * gb)123 static int64_t decode_len(GetByteContext *gb)
124 {
125     int64_t len = bytestream2_get_levarint(gb);
126 
127     if (len < 0 || len > UINT_MAX)
128         return AVERROR_INVALIDDATA;
129 
130     return len;
131 }
132 
ff_snappy_peek_uncompressed_length(GetByteContext * gb)133 int64_t ff_snappy_peek_uncompressed_length(GetByteContext *gb)
134 {
135     int pos = bytestream2_get_bytes_left(gb);
136     int64_t len = decode_len(gb);
137 
138     bytestream2_seek(gb, -pos, SEEK_END);
139 
140     return len;
141 }
142 
ff_snappy_uncompress(GetByteContext * gb,uint8_t * buf,int64_t * size)143 int ff_snappy_uncompress(GetByteContext *gb, uint8_t *buf, int64_t *size)
144 {
145     int64_t len = decode_len(gb);
146     int ret     = 0;
147     uint8_t *p;
148 
149     if (len < 0)
150         return len;
151 
152     if (len > *size)
153         return AVERROR_BUFFER_TOO_SMALL;
154 
155     *size = len;
156     p     = buf;
157 
158     while (bytestream2_get_bytes_left(gb) > 0) {
159         uint8_t s = bytestream2_get_byte(gb);
160         int val   = s >> 2;
161 
162         switch (s & 0x03) {
163         case SNAPPY_LITERAL:
164             ret = snappy_literal(gb, p, len, val);
165             break;
166         case SNAPPY_COPY_1:
167             ret = snappy_copy1(gb, buf, p, len, val);
168             break;
169         case SNAPPY_COPY_2:
170             ret = snappy_copy2(gb, buf, p, len, val);
171             break;
172         case SNAPPY_COPY_4:
173             ret = snappy_copy4(gb, buf, p, len, val);
174             break;
175         }
176 
177         if (ret < 0)
178             return ret;
179 
180         p   += ret;
181         len -= ret;
182     }
183 
184     return 0;
185 }
186