]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - lib_generic/lzo/lzo1x_decompress.c
imported Ka-Ro specific additions to U-Boot 2009.08 for TX28
[karo-tx-uboot.git] / lib_generic / lzo / lzo1x_decompress.c
1 /*
2  *  LZO1X Decompressor from MiniLZO
3  *
4  *  Copyright (C) 1996-2005 Markus F.X.J. Oberhumer <markus@oberhumer.com>
5  *
6  *  The full LZO package can be found at:
7  *  http://www.oberhumer.com/opensource/lzo/
8  *
9  *  Changed for kernel use by:
10  *  Nitin Gupta <nitingupta910@gmail.com>
11  *  Richard Purdie <rpurdie@openedhand.com>
12  */
13
14 #include <common.h>
15 #include <linux/lzo.h>
16 #include <asm/byteorder.h>
17 #include <asm/unaligned.h>
18 #include "lzodefs.h"
19
20 #define HAVE_IP(x, ip_end, ip) ((size_t)(ip_end - ip) < (x))
21 #define HAVE_OP(x, op_end, op) ((size_t)(op_end - op) < (x))
22 #define HAVE_LB(m_pos, out, op) (m_pos < out || m_pos >= op)
23
24 #define COPY4(dst, src) \
25                 put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst))
26
27 int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
28                         unsigned char *out, size_t *out_len)
29 {
30         const unsigned char * const ip_end = in + in_len;
31         unsigned char * const op_end = out + *out_len;
32         const unsigned char *ip = in, *m_pos;
33         unsigned char *op = out;
34         size_t t;
35
36         *out_len = 0;
37
38         if (*ip > 17) {
39                 t = *ip++ - 17;
40                 if (t < 4)
41                         goto match_next;
42                 if (HAVE_OP(t, op_end, op))
43                         goto output_overrun;
44                 if (HAVE_IP(t + 1, ip_end, ip))
45                         goto input_overrun;
46                 do {
47                         *op++ = *ip++;
48                 } while (--t > 0);
49                 goto first_literal_run;
50         }
51
52         while ((ip < ip_end)) {
53                 t = *ip++;
54                 if (t >= 16)
55                         goto match;
56                 if (t == 0) {
57                         if (HAVE_IP(1, ip_end, ip))
58                                 goto input_overrun;
59                         while (*ip == 0) {
60                                 t += 255;
61                                 ip++;
62                                 if (HAVE_IP(1, ip_end, ip))
63                                         goto input_overrun;
64                         }
65                         t += 15 + *ip++;
66                 }
67                 if (HAVE_OP(t + 3, op_end, op))
68                         goto output_overrun;
69                 if (HAVE_IP(t + 4, ip_end, ip))
70                         goto input_overrun;
71
72                 COPY4(op, ip);
73                 op += 4;
74                 ip += 4;
75                 if (--t > 0) {
76                         if (t >= 4) {
77                                 do {
78                                         COPY4(op, ip);
79                                         op += 4;
80                                         ip += 4;
81                                         t -= 4;
82                                 } while (t >= 4);
83                                 if (t > 0) {
84                                         do {
85                                                 *op++ = *ip++;
86                                         } while (--t > 0);
87                                 }
88                         } else {
89                                 do {
90                                         *op++ = *ip++;
91                                 } while (--t > 0);
92                         }
93                 }
94
95 first_literal_run:
96                 t = *ip++;
97                 if (t >= 16)
98                         goto match;
99                 m_pos = op - (1 + M2_MAX_OFFSET);
100                 m_pos -= t >> 2;
101                 m_pos -= *ip++ << 2;
102
103                 if (HAVE_LB(m_pos, out, op))
104                         goto lookbehind_overrun;
105
106                 if (HAVE_OP(3, op_end, op))
107                         goto output_overrun;
108                 *op++ = *m_pos++;
109                 *op++ = *m_pos++;
110                 *op++ = *m_pos;
111
112                 goto match_done;
113
114                 do {
115 match:
116                         if (t >= 64) {
117                                 m_pos = op - 1;
118                                 m_pos -= (t >> 2) & 7;
119                                 m_pos -= *ip++ << 3;
120                                 t = (t >> 5) - 1;
121                                 if (HAVE_LB(m_pos, out, op))
122                                         goto lookbehind_overrun;
123                                 if (HAVE_OP(t + 3 - 1, op_end, op))
124                                         goto output_overrun;
125                                 goto copy_match;
126                         } else if (t >= 32) {
127                                 t &= 31;
128                                 if (t == 0) {
129                                         if (HAVE_IP(1, ip_end, ip))
130                                                 goto input_overrun;
131                                         while (*ip == 0) {
132                                                 t += 255;
133                                                 ip++;
134                                                 if (HAVE_IP(1, ip_end, ip))
135                                                         goto input_overrun;
136                                         }
137                                         t += 31 + *ip++;
138                                 }
139                                 m_pos = op - 1;
140                                 m_pos -= get_unaligned_le16(ip) >> 2;
141                                 ip += 2;
142                         } else if (t >= 16) {
143                                 m_pos = op;
144                                 m_pos -= (t & 8) << 11;
145
146                                 t &= 7;
147                                 if (t == 0) {
148                                         if (HAVE_IP(1, ip_end, ip))
149                                                 goto input_overrun;
150                                         while (*ip == 0) {
151                                                 t += 255;
152                                                 ip++;
153                                                 if (HAVE_IP(1, ip_end, ip))
154                                                         goto input_overrun;
155                                         }
156                                         t += 7 + *ip++;
157                                 }
158                                 m_pos -= get_unaligned_le16(ip) >> 2;
159                                 ip += 2;
160                                 if (m_pos == op)
161                                         goto eof_found;
162                                 m_pos -= 0x4000;
163                         } else {
164                                 m_pos = op - 1;
165                                 m_pos -= t >> 2;
166                                 m_pos -= *ip++ << 2;
167
168                                 if (HAVE_LB(m_pos, out, op))
169                                         goto lookbehind_overrun;
170                                 if (HAVE_OP(2, op_end, op))
171                                         goto output_overrun;
172
173                                 *op++ = *m_pos++;
174                                 *op++ = *m_pos;
175                                 goto match_done;
176                         }
177
178                         if (HAVE_LB(m_pos, out, op))
179                                 goto lookbehind_overrun;
180                         if (HAVE_OP(t + 3 - 1, op_end, op))
181                                 goto output_overrun;
182
183                         if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) {
184                                 COPY4(op, m_pos);
185                                 op += 4;
186                                 m_pos += 4;
187                                 t -= 4 - (3 - 1);
188                                 do {
189                                         COPY4(op, m_pos);
190                                         op += 4;
191                                         m_pos += 4;
192                                         t -= 4;
193                                 } while (t >= 4);
194                                 if (t > 0)
195                                         do {
196                                                 *op++ = *m_pos++;
197                                         } while (--t > 0);
198                         } else {
199 copy_match:
200                                 *op++ = *m_pos++;
201                                 *op++ = *m_pos++;
202                                 do {
203                                         *op++ = *m_pos++;
204                                 } while (--t > 0);
205                         }
206 match_done:
207                         t = ip[-2] & 3;
208                         if (t == 0)
209                                 break;
210 match_next:
211                         if (HAVE_OP(t, op_end, op))
212                                 goto output_overrun;
213                         if (HAVE_IP(t + 1, ip_end, ip))
214                                 goto input_overrun;
215
216                         *op++ = *ip++;
217                         if (t > 1) {
218                                 *op++ = *ip++;
219                                 if (t > 2)
220                                         *op++ = *ip++;
221                         }
222
223                         t = *ip++;
224                 } while (ip < ip_end);
225         }
226
227         *out_len = op - out;
228         return LZO_E_EOF_NOT_FOUND;
229
230 eof_found:
231         *out_len = op - out;
232         return (ip == ip_end ? LZO_E_OK :
233                 (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
234 input_overrun:
235         *out_len = op - out;
236         return LZO_E_INPUT_OVERRUN;
237
238 output_overrun:
239         *out_len = op - out;
240         return LZO_E_OUTPUT_OVERRUN;
241
242 lookbehind_overrun:
243         *out_len = op - out;
244         return LZO_E_LOOKBEHIND_OVERRUN;
245 }