]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/cxgbe/cudbg/fastlz_api.c
MFV: r355716
[FreeBSD/FreeBSD.git] / sys / dev / cxgbe / cudbg / fastlz_api.c
1 /*
2    FastLZ - lightning-fast lossless compression library
3
4    Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
5    Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
6    Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
7
8    Permission is hereby granted, free of charge, to any person obtaining a copy
9    of this software and associated documentation files (the "Software"), to deal
10    in the Software without restriction, including without limitation the rights
11    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12    copies of the Software, and to permit persons to whom the Software is
13    furnished to do so, subject to the following conditions:
14
15    The above copyright notice and this permission notice shall be included in
16    all copies or substantial portions of the Software.
17
18    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24    THE SOFTWARE.
25    */
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include "osdep.h"
30 #include "cudbg.h"
31 #include "cudbg_lib_common.h"
32 #include "fastlz.h"
33
34 static unsigned char sixpack_magic[8] = {137, '6', 'P', 'K', 13, 10, 26, 10};
35
36 #define CUDBG_BLOCK_SIZE      (63*1024)
37 #define CUDBG_CHUNK_BUF_LEN   16
38 #define CUDBG_MIN_COMPR_LEN   32        /*min data length for applying compression*/
39
40 /* for Adler-32 checksum algorithm, see RFC 1950 Section 8.2 */
41
42 #define ADLER32_BASE 65521
43
44 static inline unsigned long update_adler32(unsigned long checksum,
45                                            const void *buf, int len)
46 {
47         const unsigned char *ptr = (const unsigned char *)buf;
48         unsigned long s1 = checksum & 0xffff;
49         unsigned long s2 = (checksum >> 16) & 0xffff;
50
51         while (len > 0) {
52                 unsigned k = len < 5552 ? len : 5552;
53                 len -= k;
54
55                 while (k >= 8) {
56                         s1 += *ptr++; s2 += s1;
57                         s1 += *ptr++; s2 += s1;
58                         s1 += *ptr++; s2 += s1;
59                         s1 += *ptr++; s2 += s1;
60                         s1 += *ptr++; s2 += s1;
61                         s1 += *ptr++; s2 += s1;
62                         s1 += *ptr++; s2 += s1;
63                         s1 += *ptr++; s2 += s1;
64                         k -= 8;
65                 }
66
67                 while (k-- > 0) {
68                         s1 += *ptr++; s2 += s1;
69                 }
70                 s1 = s1 % ADLER32_BASE;
71                 s2 = s2 % ADLER32_BASE;
72         }
73         return (s2 << 16) + s1;
74 }
75
76 int write_magic(struct cudbg_buffer *_out_buff)
77 {
78         int rc;
79
80         rc = write_to_buf(_out_buff->data, _out_buff->size, &_out_buff->offset,
81                           sixpack_magic, 8);
82
83         return rc;
84 }
85
86 int write_to_buf(void *out_buf, u32 out_buf_size, u32 *offset, void *in_buf,
87                  u32 in_buf_size)
88 {
89         int rc = 0;
90
91         if (*offset >= out_buf_size) {
92                 rc = CUDBG_STATUS_OUTBUFF_OVERFLOW;
93                 goto err;
94         }
95
96         memcpy((char *)out_buf + *offset, in_buf, in_buf_size);
97         *offset = *offset + in_buf_size;
98
99 err:
100         return rc;
101 }
102
103 int read_from_buf(void *in_buf, u32 in_buf_size, u32 *offset, void *out_buf,
104                   u32 out_buf_size)
105 {
106         if (in_buf_size - *offset < out_buf_size)
107                 return 0;
108
109         memcpy((char *)out_buf, (char *)in_buf + *offset, out_buf_size);
110         *offset =  *offset + out_buf_size;
111         return out_buf_size;
112 }
113
114 int write_chunk_header(struct cudbg_buffer *_outbuf, int id, int options,
115                        unsigned long size, unsigned long checksum,
116                        unsigned long extra)
117 {
118         unsigned char buffer[CUDBG_CHUNK_BUF_LEN];
119         int rc = 0;
120
121         buffer[0] = id & 255;
122         buffer[1] = (unsigned char)(id >> 8);
123         buffer[2] = options & 255;
124         buffer[3] = (unsigned char)(options >> 8);
125         buffer[4] = size & 255;
126         buffer[5] = (size >> 8) & 255;
127         buffer[6] = (size >> 16) & 255;
128         buffer[7] = (size >> 24) & 255;
129         buffer[8] = checksum & 255;
130         buffer[9] = (checksum >> 8) & 255;
131         buffer[10] = (checksum >> 16) & 255;
132         buffer[11] = (checksum >> 24) & 255;
133         buffer[12] = extra & 255;
134         buffer[13] = (extra >> 8) & 255;
135         buffer[14] = (extra >> 16) & 255;
136         buffer[15] = (extra >> 24) & 255;
137
138         rc = write_to_buf(_outbuf->data, _outbuf->size, &_outbuf->offset,
139                           buffer, 16);
140
141         return rc;
142 }
143
144 int write_compression_hdr(struct cudbg_buffer *pin_buff,
145                           struct cudbg_buffer *pout_buff)
146 {
147         struct cudbg_buffer tmp_buffer;
148         unsigned long fsize = pin_buff->size;
149         unsigned char *buffer;
150         unsigned long checksum;
151         int rc;
152         char *shown_name = "abc";
153
154         /* Always release inner scratch buffer, before releasing outer. */
155         rc = get_scratch_buff(pout_buff, 10, &tmp_buffer);
156
157         if (rc)
158                 goto err;
159
160         buffer = (unsigned char *)tmp_buffer.data;
161
162         rc = write_magic(pout_buff);
163
164         if (rc)
165                 goto err1;
166
167         /* chunk for File Entry */
168         buffer[0] = fsize & 255;
169         buffer[1] = (fsize >> 8) & 255;
170         buffer[2] = (fsize >> 16) & 255;
171         buffer[3] = (fsize >> 24) & 255;
172         buffer[4] = 0;
173         buffer[5] = 0;
174         buffer[6] = 0;
175         buffer[7] = 0;
176         buffer[8] = (strlen(shown_name)+1) & 255;
177         buffer[9] = (unsigned char)((strlen(shown_name)+1) >> 8);
178         checksum = 1L;
179         checksum = update_adler32(checksum, buffer, 10);
180         checksum = update_adler32(checksum, shown_name,
181                                   (int)strlen(shown_name)+1);
182
183         rc = write_chunk_header(pout_buff, 1, 0,
184                                 10+(unsigned long)strlen(shown_name)+1,
185                                 checksum, 0);
186
187         if (rc)
188                 goto err1;
189
190         rc = write_to_buf(pout_buff->data, pout_buff->size,
191                           &(pout_buff->offset), buffer, 10);
192
193         if (rc)
194                 goto err1;
195
196         rc = write_to_buf(pout_buff->data, pout_buff->size,
197                            &(pout_buff->offset), shown_name,
198                            (u32)strlen(shown_name)+1);
199
200         if (rc)
201                 goto err1;
202
203 err1:
204         release_scratch_buff(&tmp_buffer, pout_buff);
205 err:
206         return rc;
207 }
208
209 int compress_buff(struct cudbg_buffer *pin_buff, struct cudbg_buffer *pout_buff)
210 {
211         struct cudbg_buffer tmp_buffer;
212         struct cudbg_hdr *cudbg_hdr;
213         unsigned long checksum;
214         unsigned char *result;
215         unsigned int bytes_read;
216         int chunk_size, level = 2, rc = 0;
217         int compress_method = 1;
218
219         bytes_read = pin_buff->size;
220         rc = get_scratch_buff(pout_buff, CUDBG_BLOCK_SIZE, &tmp_buffer);
221
222         if (rc)
223                 goto err;
224
225         result = (unsigned char *)tmp_buffer.data;
226
227         if (bytes_read < 32)
228                 compress_method = 0;
229
230         cudbg_hdr = (struct cudbg_hdr *)  pout_buff->data;
231
232         switch (compress_method) {
233         case 1:
234                 chunk_size = fastlz_compress_level(level, pin_buff->data,
235                                                    bytes_read, result);
236
237                 checksum = update_adler32(1L, result, chunk_size);
238
239                 if ((chunk_size > 62000) && (cudbg_hdr->reserved[7] < (u32)
240                     chunk_size))   /* 64512 */
241                         cudbg_hdr->reserved[7] = (u32) chunk_size;
242
243                 rc = write_chunk_header(pout_buff, 17, 1, chunk_size, checksum,
244                                         bytes_read);
245
246                 if (rc)
247                         goto err_put_buff;
248
249                 rc = write_to_buf(pout_buff->data, pout_buff->size,
250                                   &pout_buff->offset, result, chunk_size);
251
252                 if (rc)
253                         goto err_put_buff;
254
255                 break;
256
257                 /* uncompressed, also fallback method */
258         case 0:
259         default:
260                 checksum = update_adler32(1L, pin_buff->data, bytes_read);
261
262                 rc = write_chunk_header(pout_buff, 17, 0, bytes_read, checksum,
263                                         bytes_read);
264
265                 if (rc)
266                         goto err_put_buff;
267
268                 rc = write_to_buf(pout_buff->data, pout_buff->size,
269                                   &pout_buff->offset, pin_buff->data,
270                                   bytes_read);
271                 if (rc)
272                         goto err_put_buff;
273
274                 break;
275         }
276
277 err_put_buff:
278         release_scratch_buff(&tmp_buffer, pout_buff);
279 err:
280         return rc;
281 }
282
283 /* return non-zero if magic sequence is detected */
284 /* warning: reset the read pointer to the beginning of the file */
285 int detect_magic(struct cudbg_buffer *_c_buff)
286 {
287         unsigned char buffer[8];
288         size_t bytes_read;
289         int c;
290
291         bytes_read = read_from_buf(_c_buff->data, _c_buff->size,
292                                    &_c_buff->offset, buffer, 8);
293
294         if (bytes_read < 8)
295                 return 0;
296
297         for (c = 0; c < 8; c++)
298                 if (buffer[c] != sixpack_magic[c])
299                         return 0;
300
301         return -1;
302 }
303
304 static inline unsigned long readU16(const unsigned char *ptr)
305 {
306         return ptr[0]+(ptr[1]<<8);
307 }
308
309 static inline unsigned long readU32(const unsigned char *ptr)
310 {
311         return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
312 }
313
314 int read_chunk_header(struct cudbg_buffer *pc_buff, int *pid, int *poptions,
315                       unsigned long *psize, unsigned long *pchecksum,
316                       unsigned long *pextra)
317 {
318         unsigned char buffer[CUDBG_CHUNK_BUF_LEN];
319         int byte_r = read_from_buf(pc_buff->data, pc_buff->size,
320                                    &pc_buff->offset, buffer, 16);
321         if (byte_r == 0)
322                 return 0;
323
324         *pid = readU16(buffer) & 0xffff;
325         *poptions = readU16(buffer+2) & 0xffff;
326         *psize = readU32(buffer+4) & 0xffffffff;
327         *pchecksum = readU32(buffer+8) & 0xffffffff;
328         *pextra = readU32(buffer+12) & 0xffffffff;
329         return 0;
330 }
331
332 int validate_buffer(struct cudbg_buffer *compressed_buffer)
333 {
334         if (!detect_magic(compressed_buffer))
335                 return CUDBG_STATUS_INVALID_BUFF;
336
337         return 0;
338 }
339
340 int decompress_buffer(struct cudbg_buffer *pc_buff,
341                       struct cudbg_buffer *pd_buff)
342 {
343         struct cudbg_buffer tmp_compressed_buffer;
344         struct cudbg_buffer tmp_decompressed_buffer;
345         unsigned char *compressed_buffer;
346         unsigned char *decompressed_buffer;
347         unsigned char buffer[CUDBG_MIN_COMPR_LEN];
348         unsigned long chunk_size;
349         unsigned long chunk_checksum;
350         unsigned long chunk_extra;
351         unsigned long checksum;
352         unsigned long total_extracted = 0;
353         unsigned long r;
354         unsigned long remaining;
355         unsigned long bytes_read;
356         u32 decompressed_size = 0;
357         int chunk_id, chunk_options, rc;
358
359         if (pd_buff->size < 2 * CUDBG_BLOCK_SIZE)
360                 return CUDBG_STATUS_SMALL_BUFF;
361
362         rc = get_scratch_buff(pd_buff, CUDBG_BLOCK_SIZE,
363                               &tmp_compressed_buffer);
364
365         if (rc)
366                 goto err_cbuff;
367
368         rc = get_scratch_buff(pd_buff, CUDBG_BLOCK_SIZE,
369                               &tmp_decompressed_buffer);
370         if (rc)
371                 goto err_dcbuff;
372
373         compressed_buffer = (unsigned char *)tmp_compressed_buffer.data;
374         decompressed_buffer = (unsigned char *)tmp_decompressed_buffer.data;
375
376         /* main loop */
377
378         for (;;) {
379                 if (pc_buff->offset > pc_buff->size)
380                         break;
381
382                 rc =  read_chunk_header(pc_buff, &chunk_id, &chunk_options,
383                                         &chunk_size, &chunk_checksum,
384                                         &chunk_extra);
385                 if (rc != 0)
386                         break;
387
388                 /* skip 8+16 */
389                 if ((chunk_id == 1) && (chunk_size > 10) &&
390                     (chunk_size < CUDBG_BLOCK_SIZE)) {
391
392                         bytes_read = read_from_buf(pc_buff->data, pc_buff->size,
393                                                    &pc_buff->offset, buffer,
394                                                    chunk_size);
395
396                         if (bytes_read == 0)
397                                 return 0;
398
399                         checksum = update_adler32(1L, buffer, chunk_size);
400                         if (checksum != chunk_checksum)
401                                 return CUDBG_STATUS_CHKSUM_MISSMATCH;
402
403                         decompressed_size = (u32)readU32(buffer);
404
405                         if (pd_buff->size < decompressed_size) {
406
407                                 pd_buff->size = 2 * CUDBG_BLOCK_SIZE +
408                                                 decompressed_size;
409                                 pc_buff->offset -= chunk_size + 16;
410                                 return CUDBG_STATUS_SMALL_BUFF;
411                         }
412                         total_extracted = 0;
413
414                 }
415
416                 if (chunk_size > CUDBG_BLOCK_SIZE) {
417                         /* Release old allocated memory */
418                         release_scratch_buff(&tmp_decompressed_buffer, pd_buff);
419                         release_scratch_buff(&tmp_compressed_buffer, pd_buff);
420
421                         /* allocate new memory with chunk_size size */
422                         rc = get_scratch_buff(pd_buff, chunk_size,
423                                               &tmp_compressed_buffer);
424                         if (rc)
425                                 goto err_cbuff;
426
427                         rc = get_scratch_buff(pd_buff, chunk_size,
428                                               &tmp_decompressed_buffer);
429                         if (rc)
430                                 goto err_dcbuff;
431
432                         compressed_buffer = (unsigned char *)tmp_compressed_buffer.data;
433                         decompressed_buffer = (unsigned char *)tmp_decompressed_buffer.data;
434                 }
435
436                 if ((chunk_id == 17) && decompressed_size) {
437                         /* uncompressed */
438                         switch (chunk_options) {
439                                 /* stored, simply copy to output */
440                         case 0:
441                                 total_extracted += chunk_size;
442                                 remaining = chunk_size;
443                                 checksum = 1L;
444                                 for (;;) {
445                                         /* Write a funtion for this */
446                                         r = (CUDBG_BLOCK_SIZE < remaining) ?
447                                             CUDBG_BLOCK_SIZE : remaining;
448                                         bytes_read =
449                                         read_from_buf(pc_buff->data,
450                                                       pc_buff->size,
451                                                       &pc_buff->offset, buffer,
452                                                       r);
453
454                                         if (bytes_read == 0)
455                                                 return 0;
456
457                                         write_to_buf(pd_buff->data,
458                                                      pd_buff->size,
459                                                      &pd_buff->offset, buffer,
460                                                      bytes_read);
461                                         checksum = update_adler32(checksum,
462                                                                   buffer,
463                                                                   bytes_read);
464                                         remaining -= bytes_read;
465
466                                         /* verify everything is written
467                                          * correctly */
468                                         if (checksum != chunk_checksum)
469                                                 return
470                                                 CUDBG_STATUS_CHKSUM_MISSMATCH;
471                                 }
472
473                                 break;
474
475                                 /* compressed using FastLZ */
476                         case 1:
477                                 bytes_read = read_from_buf(pc_buff->data,
478                                                            pc_buff->size,
479                                                            &pc_buff->offset,
480                                                            compressed_buffer,
481                                                            chunk_size);
482
483                                 if (bytes_read == 0)
484                                         return 0;
485
486                                 checksum = update_adler32(1L, compressed_buffer,
487                                                           chunk_size);
488                                 total_extracted += chunk_extra;
489
490                                 /* verify that the chunk data is correct */
491                                 if (checksum != chunk_checksum) {
492                                         return CUDBG_STATUS_CHKSUM_MISSMATCH;
493                                 } else {
494                                         /* decompress and verify */
495                                         remaining =
496                                         fastlz_decompress(compressed_buffer,
497                                                           chunk_size,
498                                                           decompressed_buffer,
499                                                           chunk_extra);
500
501                                         if (remaining != chunk_extra) {
502                                                 rc =
503                                                 CUDBG_STATUS_DECOMPRESS_FAIL;
504                                                 goto err;
505                                         } else {
506                                                 write_to_buf(pd_buff->data,
507                                                              pd_buff->size,
508                                                              &pd_buff->offset,
509                                                              decompressed_buffer,
510                                                              chunk_extra);
511                                         }
512                                 }
513                                 break;
514
515                         default:
516                                 break;
517                         }
518
519                 }
520
521         }
522
523 err:
524         release_scratch_buff(&tmp_decompressed_buffer, pd_buff);
525 err_dcbuff:
526         release_scratch_buff(&tmp_compressed_buffer, pd_buff);
527
528 err_cbuff:
529         return rc;
530 }
531