1/* lzo1x_oo.ch -- LZO1X compressed data optimizer
2
3   This file is part of the LZO real-time data compression library.
4
5   Copyright (C) 1996-2014 Markus Franz Xaver Johannes Oberhumer
6   All Rights Reserved.
7
8   The LZO library is free software; you can redistribute it and/or
9   modify it under the terms of the GNU General Public License as
10   published by the Free Software Foundation; either version 2 of
11   the License, or (at your option) any later version.
12
13   The LZO library is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with the LZO library; see the file COPYING.
20   If not, write to the Free Software Foundation, Inc.,
21   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
23   Markus F.X.J. Oberhumer
24   <markus@oberhumer.com>
25   http://www.oberhumer.com/opensource/lzo/
26 */
27
28
29#define TEST_IP     (ip < ip_end)
30#define TEST_OP     (op <= op_end)
31#define TEST_IP_AND_TEST_OP (TEST_IP && TEST_OP)
32
33#define NO_LIT      LZO_UINT_MAX
34
35
36/***********************************************************************
37//
38************************************************************************/
39
40static void copy2(lzo_bytep ip, const lzo_bytep m_pos, lzo_uint off)
41{
42    assert(off > 0);
43    ip[0] = m_pos[0];
44    if (off == 1)
45        ip[1] = m_pos[0];
46    else
47        ip[1] = m_pos[1];
48}
49
50
51static void copy3(lzo_bytep ip, const lzo_bytep m_pos, lzo_uint off)
52{
53    assert(off > 0);
54    ip[0] = m_pos[0];
55    if (off == 1)
56    {
57        ip[2] = ip[1] = m_pos[0];
58    }
59    else if (off == 2)
60    {
61        ip[1] = m_pos[1];
62        ip[2] = m_pos[0];
63    }
64    else
65    {
66        ip[1] = m_pos[1];
67        ip[2] = m_pos[2];
68    }
69}
70
71
72/***********************************************************************
73// optimize a block of data.
74************************************************************************/
75
76LZO_PUBLIC(int)
77DO_OPTIMIZE          (       lzo_bytep in , lzo_uint  in_len,
78                             lzo_bytep out, lzo_uintp out_len,
79                             lzo_voidp wrkmem )
80{
81    lzo_bytep op;
82    lzo_bytep ip;
83    lzo_uint t;
84    lzo_bytep m_pos;
85    lzo_bytep const ip_end = in + in_len;
86    lzo_bytep const op_end = out + *out_len;
87    lzo_bytep litp = NULL;
88    lzo_uint lit = 0;
89    lzo_uint next_lit = NO_LIT;
90    lzo_uint nl;
91    unsigned long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0;
92
93    LZO_UNUSED(wrkmem);
94
95    *out_len = 0;
96
97    op = out;
98    ip = in;
99
100    assert(in_len >= 3);
101    if (*ip > 17)
102    {
103        t = *ip++ - 17;
104        if (t < 4)
105            goto match_next;
106        goto first_literal_run;
107    }
108    assert(*ip < 16 || (*ip == 17 && in_len == 3));
109
110    while (TEST_IP_AND_TEST_OP)
111    {
112        t = *ip++;
113        if (t >= 16)
114            goto match;
115        /* a literal run */
116        litp = ip - 1;
117        if (t == 0)
118        {
119            t = 15;
120            while (*ip == 0)
121                t += 255, ip++;
122            t += *ip++;
123        }
124        lit = t + 3;
125        /* copy literals */
126copy_literal_run:
127        *op++ = *ip++; *op++ = *ip++; *op++ = *ip++;
128first_literal_run:
129        do *op++ = *ip++; while (--t > 0);
130
131
132        t = *ip++;
133
134        if (t >= 16)
135            goto match;
136#if defined(LZO1X)
137        m_pos = op - 1 - 0x800;
138#elif defined(LZO1Y)
139        m_pos = op - 1 - 0x400;
140#endif
141        m_pos -= t >> 2;
142        m_pos -= *ip++ << 2;
143        *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos++;
144        lit = 0;
145        goto match_done;
146
147
148        /* handle matches */
149        do {
150            if (t < 16)                     /* a M1 match */
151            {
152                m_pos = op - 1;
153                m_pos -= t >> 2;
154                m_pos -= *ip++ << 2;
155
156                if (litp == NULL)
157                    goto copy_m1;
158
159                /* assert that there was a match just before */
160                assert(lit >= 1 && lit <= 3);
161                assert(litp == ip - 2 - lit - 2);
162                assert((lzo_uint)(*litp & 3) == lit);
163                nl = ip[-2] & 3;
164                /* test if a match follows */
165                if (nl == 0 && lit == 1 && ip[0] >= 16)
166                {
167                    next_lit = nl;
168                    /* adjust length of previous short run */
169                    lit += 2;
170                    *litp = LZO_BYTE((*litp & ~3) | lit);
171                    /* copy over the 2 literals that replace the match */
172                    copy2(ip-2,m_pos,pd(op,m_pos));
173                    o_m1_a++;
174                }
175                /* test if a literal run follows */
176                else if (nl == 0 && ip[0] < 16 && ip[0] != 0 &&
177                         (lit + 2 + ip[0] < 16))
178                {
179                    t = *ip++;
180                    /* remove short run */
181                    *litp &= ~3;
182                    /* copy over the 2 literals that replace the match */
183                    copy2(ip-3+1,m_pos,pd(op,m_pos));
184                    /* move literals 1 byte ahead */
185                    litp += 2;
186                    if (lit > 0)
187                        lzo_memmove(litp+1,litp,lit);
188                    /* insert new length of long literal run */
189                    lit += 2 + t + 3; assert(lit <= 18);
190                    *litp = LZO_BYTE(lit - 3);
191
192                    o_m1_b++;
193                    *op++ = *m_pos++; *op++ = *m_pos++;
194                    goto copy_literal_run;
195                }
196copy_m1:
197                *op++ = *m_pos++; *op++ = *m_pos++;
198            }
199            else
200            {
201match:
202                if (t >= 64)                /* a M2 match */
203                {
204                    m_pos = op - 1;
205#if defined(LZO1X)
206                    m_pos -= (t >> 2) & 7;
207                    m_pos -= *ip++ << 3;
208                    t = (t >> 5) - 1;
209#elif defined(LZO1Y)
210                    m_pos -= (t >> 2) & 3;
211                    m_pos -= *ip++ << 2;
212                    t = (t >> 4) - 3;
213#endif
214                    if (litp == NULL)
215                        goto copy_m;
216
217                    nl = ip[-2] & 3;
218                    /* test if in beetween two long literal runs */
219                    if (t == 1 && lit > 3 && nl == 0 &&
220                        ip[0] < 16 && ip[0] != 0 && (lit + 3 + ip[0] < 16))
221                    {
222                        assert(*litp == lit - 3);
223                        t = *ip++;
224                        /* copy over the 3 literals that replace the match */
225                        copy3(ip-1-2,m_pos,pd(op,m_pos));
226                        /* set new length of previous literal run */
227                        lit += 3 + t + 3; assert(lit <= 18);
228                        *litp = LZO_BYTE(lit - 3);
229                        o_m2++;
230                        *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos++;
231                        goto copy_literal_run;
232                    }
233                }
234                else
235                {
236                    if (t >= 32)            /* a M3 match */
237                    {
238                        t &= 31;
239                        if (t == 0)
240                        {
241                            t = 31;
242                            while (*ip == 0)
243                                t += 255, ip++;
244                            t += *ip++;
245                        }
246                        m_pos = op - 1;
247                        m_pos -= *ip++ >> 2;
248                        m_pos -= *ip++ << 6;
249                    }
250                    else                    /* a M4 match */
251                    {
252                        m_pos = op;
253                        m_pos -= (t & 8) << 11;
254                        t &= 7;
255                        if (t == 0)
256                        {
257                            t = 7;
258                            while (*ip == 0)
259                                t += 255, ip++;
260                            t += *ip++;
261                        }
262                        m_pos -= *ip++ >> 2;
263                        m_pos -= *ip++ << 6;
264                        if (m_pos == op)
265                            goto eof_found;
266                        m_pos -= 0x4000;
267                    }
268                    if (litp == NULL)
269                        goto copy_m;
270
271                    nl = ip[-2] & 3;
272                    /* test if in beetween two matches */
273                    if (t == 1 && lit == 0 && nl == 0 && ip[0] >= 16)
274                    {
275                        assert(litp == ip - 3 - lit - 2);
276                        assert((lzo_uint)(*litp & 3) == lit);
277                        next_lit = nl;
278                        /* make a previous short run */
279                        lit += 3;
280                        *litp = LZO_BYTE((*litp & ~3) | lit);
281                        /* copy over the 3 literals that replace the match */
282                        copy3(ip-3,m_pos,pd(op,m_pos));
283                        o_m3_a++;
284                    }
285                    /* test if a literal run follows */
286                    else if (t == 1 && lit <= 3 && nl == 0 &&
287                             ip[0] < 16 && ip[0] != 0 && (lit + 3 + ip[0] < 16))
288                    {
289                        assert(litp == ip - 3 - lit - 2);
290                        assert((lzo_uint)(*litp & 3) == lit);
291                        t = *ip++;
292                        /* remove short run */
293                        *litp &= ~3;
294                        /* copy over the 3 literals that replace the match */
295                        copy3(ip-4+1,m_pos,pd(op,m_pos));
296                        /* move literals 1 byte ahead */
297                        litp += 2;
298                        if (lit > 0)
299                            lzo_memmove(litp+1,litp,lit);
300                        /* insert new length of long literal run */
301                        lit += 3 + t + 3; assert(lit <= 18);
302                        *litp = LZO_BYTE(lit - 3);
303
304                        o_m3_b++;
305                        *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos++;
306                        goto copy_literal_run;
307                    }
308                }
309copy_m:
310                *op++ = *m_pos++; *op++ = *m_pos++;
311                do *op++ = *m_pos++; while (--t > 0);
312            }
313
314match_done:
315            if (next_lit == NO_LIT)
316            {
317                t = ip[-2] & 3;
318                lit = t;
319                litp = ip - 2;
320            }
321            else
322                t = next_lit;
323            assert(t <= 3);
324            next_lit = NO_LIT;
325            if (t == 0)
326                break;
327            /* copy literals */
328match_next:
329            do *op++ = *ip++; while (--t > 0);
330            t = *ip++;
331        } while (TEST_IP_AND_TEST_OP);
332    }
333
334    /* no EOF code was found */
335    *out_len = pd(op, out);
336    return LZO_E_EOF_NOT_FOUND;
337
338eof_found:
339    assert(t == 1);
340#if 0
341    printf("optimize: %5lu %5lu   %5lu   %5lu %5lu\n",
342           o_m1_a, o_m1_b, o_m2, o_m3_a, o_m3_b);
343#endif
344    LZO_UNUSED(o_m1_a); LZO_UNUSED(o_m1_b); LZO_UNUSED(o_m2);
345    LZO_UNUSED(o_m3_a); LZO_UNUSED(o_m3_b);
346    *out_len = pd(op, out);
347    return (ip == ip_end ? LZO_E_OK :
348           (ip < ip_end  ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
349}
350
351
352/*
353vi:ts=4:et
354*/
355
356