lz4s.c
Go to the documentation of this file.
00001 /*********************************************************************
00002 * Software License Agreement (BSD License)
00003 *
00004 *  Copyright (c) 2014, Ben Charrow
00005 *  All rights reserved.
00006 *
00007 *  Redistribution and use in source and binary forms, with or without
00008 *  modification, are permitted provided that the following conditions
00009 *  are met:
00010 *
00011 *   * Redistributions of source code must retain the above copyright
00012 *     notice, this list of conditions and the following disclaimer.
00013 *   * Redistributions in binary form must reproduce the above
00014 *     copyright notice, this list of conditions and the following
00015 *     disclaimer in the documentation and/or other materials provided
00016 *     with the distribution.
00017 *   * Neither the name of Willow Garage, Inc. nor the names of its
00018 *     contributors may be used to endorse or promote products derived
00019 *     from this software without specific prior written permission.
00020 *
00021 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032 *  POSSIBILITY OF SUCH DAMAGE.
00033 ********************************************************************/
00034 
00035 #include "roslz4/lz4s.h"
00036 
00037 #include "xxhash.h"
00038 
00039 #include <stdint.h>
00040 #include <string.h>
00041 #include <stdio.h>
00042 #include <stdlib.h>
00043 
00044 #if 0
00045 #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
00046 #else
00047 #define DEBUG(...)
00048 #endif
00049 
00050 // magic numbers
00051 const uint32_t kMagicNumber = 0x184D2204;
00052 const uint32_t kEndOfStream = 0x00000000;
00053 
00054 // Bitmasks
00055 const uint8_t k1Bits = 0x01;
00056 const uint8_t k2Bits = 0x03;
00057 const uint8_t k3Bits = 0x07;
00058 const uint8_t k4Bits = 0x0F;
00059 const uint8_t k8Bits = 0xFF;
00060 
00061 uint32_t readUInt32(unsigned char *buffer) {
00062   return ((buffer[0] <<  0) | (buffer[1] <<  8) |
00063           (buffer[2] << 16) | (buffer[3] << 24));
00064 }
00065 
00066 void writeUInt32(unsigned char *buffer, uint32_t val) {
00067   buffer[0] = val & 0xFF;
00068   buffer[1] = (val >> 8) & 0xFF;
00069   buffer[2] = (val >> 16) & 0xFF;
00070   buffer[3] = (val >> 24) & 0xFF;
00071 }
00072 
00073 int min(int a, int b) {
00074   return a < b ? a : b;
00075 }
00076 
00077 /*========================== Low level compression ==========================*/
00078 
00079 typedef struct {
00080   int block_independence_flag;
00081   int block_checksum_flag;
00082   int stream_checksum_flag;
00083 
00084   char *buffer;
00085   int buffer_size;
00086   int buffer_offset;
00087 
00088   int finished; // 1 if done compressing/decompressing; 0 otherwise
00089 
00090   void* xxh32_state;
00091 
00092   // Compression state
00093   int wrote_header;
00094 
00095   // Decompression state
00096   char header[10];
00097   uint32_t block_size; // Size of current block
00098   int block_size_read; // # of bytes read for current block_size
00099   int block_uncompressed; // 1 if block is uncompressed, 0 otherwise
00100   uint32_t stream_checksum; // Storage for checksum
00101   int stream_checksum_read; // # of bytes read for stream_checksum
00102 } stream_state;
00103 
00104 void advanceInput(roslz4_stream *str, int nbytes) {
00105   str->input_next += nbytes;
00106   str->input_left -= nbytes;
00107   str->total_in += nbytes;
00108 }
00109 
00110 void advanceOutput(roslz4_stream *str, int nbytes) {
00111   str->output_next += nbytes;
00112   str->output_left -= nbytes;
00113   str->total_out += nbytes;
00114 }
00115 
00116 void fillUInt32(roslz4_stream *str, uint32_t *dest_val, int *offset) {
00117   char *dest = (char*) dest_val;
00118   int to_copy = min(4 - *offset, str->input_left);
00119   memcpy(dest + *offset, str->input_next, to_copy);
00120   advanceInput(str, to_copy);
00121   *offset += to_copy;
00122 }
00123 
00124 int writeHeader(roslz4_stream *str) {
00125   if (str->output_left < 7) {
00126     return ROSLZ4_OUTPUT_SMALL; // Output must have 7 bytes
00127   }
00128 
00129   stream_state *state = str->state;
00130   writeUInt32((unsigned char*) str->output_next, kMagicNumber);
00131   int version = 1;
00132   char *out = str->output_next;
00133   *(out+4)  = ((unsigned)version                        & k2Bits) << 6;
00134   *(out+4) |= ((unsigned)state->block_independence_flag & k1Bits) << 5;
00135   *(out+4) |= ((unsigned)state->block_checksum_flag     & k1Bits) << 4;
00136   *(out+4) |= ((unsigned)state->stream_checksum_flag    & k1Bits) << 2;
00137   *(out+5)  = ((unsigned)str->block_size_id             & k3Bits) << 4;
00138 
00139   // Checksum: 2nd byte of hash of header flags
00140   unsigned char checksum = (XXH32(str->output_next + 4, 2, 0) >> 8) & k8Bits;
00141   *(str->output_next+6) = checksum;
00142 
00143   advanceOutput(str, 7);
00144   DEBUG("writeHeader() Put 7 bytes in output\n");
00145 
00146   return ROSLZ4_OK;
00147 }
00148 
00149 int writeEOS(roslz4_stream *str) {
00150   if (str->output_left < 8) {
00151     return ROSLZ4_OUTPUT_SMALL;
00152   }
00153 
00154   stream_state *state = str->state;
00155   state->finished = 1;
00156   writeUInt32((unsigned char*) str->output_next, kEndOfStream);
00157   advanceOutput(str, 4);
00158 
00159   uint32_t stream_checksum = XXH32_digest(state->xxh32_state);
00160   writeUInt32((unsigned char*) str->output_next, stream_checksum);
00161   advanceOutput(str, 4);
00162   state->xxh32_state = NULL;
00163 
00164   DEBUG("writeEOS() Wrote 8 bytes to output %i\n", str->output_left);
00165   return ROSLZ4_STREAM_END;
00166 }
00167 
00168 // If successfull, number of bytes written to output
00169 // If error, LZ4 return code
00170 int bufferToOutput(roslz4_stream *str) {
00171   stream_state *state = str->state;
00172   uint32_t uncomp_size = state->buffer_offset;
00173   if (state->buffer_offset == 0) {
00174     return 0; // No data to flush
00175   } else if (str->output_left - 4 < uncomp_size) {
00176     DEBUG("bufferToOutput() Not enough space left in output\n");
00177     return ROSLZ4_OUTPUT_SMALL;
00178   }
00179 
00180   DEBUG("bufferToOutput() Flushing %i bytes, %i left in output\n",
00181         state->buffer_offset, str->output_left);
00182 
00183   // Shrink output by 1 to detect if data is not compressible
00184   uint32_t comp_size = LZ4_compress_limitedOutput(state->buffer,
00185                                                   str->output_next + 4,
00186                                                   (int) state->buffer_offset,
00187                                                   (int) uncomp_size - 1);
00188   uint32_t wrote;
00189   if (comp_size > 0) {
00190     DEBUG("bufferToOutput() Compressed to %i bytes\n", comp_size);
00191     // Write compressed data size
00192     wrote = 4 + comp_size;
00193     writeUInt32((unsigned char*)str->output_next, comp_size);
00194   } else {
00195     // Write uncompressed data
00196     DEBUG("bufferToOutput() Can't compress, copying input\n");
00197     memcpy(str->output_next + 4, state->buffer, uncomp_size);
00198     // Write uncompressed data size.  Signal data is uncompressed with high
00199     // order bit; won't confuse decompression because max block size is < 2GB
00200     wrote = 4 + uncomp_size;
00201     writeUInt32((unsigned char*) str->output_next, uncomp_size | 0x80000000);
00202   }
00203 
00204   advanceOutput(str, wrote);
00205   state->buffer_offset -= uncomp_size;
00206 
00207   DEBUG("bufferToOutput() Ate %i from buffer, wrote %i to output (%i)\n",
00208         uncomp_size, wrote, str->output_left);
00209   return wrote;
00210 }
00211 
00212 // Copy as much data as possible from input to internal buffer
00213 // Return number of bytes written if successful, LZ4 error code on error
00214 int inputToBuffer(roslz4_stream *str) {
00215   stream_state *state = str->state;
00216   if (str->input_left == 0 ||
00217       state->buffer_size == state->buffer_offset) {
00218     return 0;
00219   }
00220   int buffer_left = state->buffer_size - state->buffer_offset;
00221   int to_copy = min(str->input_left, buffer_left);
00222 
00223   int ret = XXH32_update(state->xxh32_state, str->input_next, to_copy);
00224   if (ret == XXH_ERROR) { return ROSLZ4_ERROR; }
00225 
00226   memcpy(state->buffer + state->buffer_offset, str->input_next, to_copy);
00227   advanceInput(str, to_copy);
00228   state->buffer_offset += to_copy;
00229 
00230   DEBUG("inputToBuffer() Wrote % 5i bytes to buffer (size=% 5i)\n",
00231         to_copy, state->buffer_offset);
00232   return to_copy;
00233 }
00234 
00235 int streamStateAlloc(roslz4_stream *str) {
00236   stream_state *state = (stream_state*) malloc(sizeof(stream_state));
00237   if (state == NULL) {
00238     return ROSLZ4_MEMORY_ERROR; // Allocation of state failed
00239   }
00240   str->state = state;
00241 
00242   str->block_size_id = -1;
00243   state->block_independence_flag = 1;
00244   state->block_checksum_flag = 0;
00245   state->stream_checksum_flag = 1;
00246 
00247   state->finished = 0;
00248 
00249   state->xxh32_state = XXH32_init(0);
00250   state->stream_checksum = 0;
00251   state->stream_checksum_read = 0;
00252 
00253   state->wrote_header = 0;
00254 
00255   state->buffer_offset = 0;
00256   state->buffer_size = 0;
00257   state->buffer = NULL;
00258 
00259   state->block_size = 0;
00260   state->block_size_read = 0;
00261   state->block_uncompressed = 0;
00262 
00263   str->total_in = 0;
00264   str->total_out = 0;
00265 
00266   return ROSLZ4_OK;
00267 }
00268 
00269 int streamResizeBuffer(roslz4_stream *str, int block_size_id) {
00270   stream_state *state = str->state;
00271   if (!(4 <= block_size_id && block_size_id <= 7)) {
00272     return ROSLZ4_PARAM_ERROR; // Invalid block size
00273   }
00274 
00275   str->block_size_id = block_size_id;
00276   state->buffer_offset = 0;
00277   state->buffer_size = roslz4_blockSizeFromIndex(str->block_size_id);
00278   state->buffer = (char*) malloc(sizeof(char) * state->buffer_size);
00279   if (state->buffer == NULL) {
00280     return ROSLZ4_MEMORY_ERROR; // Allocation of buffer failed
00281   }
00282   return ROSLZ4_OK;
00283 }
00284 
00285 void streamStateFree(roslz4_stream *str) {
00286   stream_state *state = str->state;
00287   if (state != NULL) {
00288     if (state->buffer != NULL) {
00289       free(state->buffer);
00290     }
00291     if (state->xxh32_state != NULL) {
00292       XXH32_digest(state->xxh32_state);
00293     }
00294     free(state);
00295     str->state = NULL;
00296   }
00297 }
00298 
00299 int roslz4_blockSizeFromIndex(int block_id) {
00300   return (1 << (8 + (2 * block_id)));
00301 }
00302 
00303 int roslz4_compressStart(roslz4_stream *str, int block_size_id) {
00304   int ret = streamStateAlloc(str);
00305   if (ret < 0) { return ret; }
00306   return streamResizeBuffer(str, block_size_id);
00307 }
00308 
00309 int roslz4_compress(roslz4_stream *str, int action) {
00310   int ret;
00311   stream_state *state = str->state;
00312   if (action != ROSLZ4_RUN && action != ROSLZ4_FINISH) {
00313     return ROSLZ4_PARAM_ERROR; // Unrecognized compression action
00314   } else if (state->finished) {
00315     return ROSLZ4_ERROR; // Cannot call action on finished stream
00316   }
00317 
00318   if (!state->wrote_header) {
00319     ret = writeHeader(str);
00320     if (ret < 0) { return ret; }
00321     state->wrote_header = 1;
00322   }
00323 
00324   // Copy input to internal buffer, compressing when full or finishing stream
00325   int read = 0, wrote = 0;
00326   do {
00327     read = inputToBuffer(str);
00328     if (read < 0) { return read; }
00329 
00330     wrote = 0;
00331     if (action == ROSLZ4_FINISH || state->buffer_offset == state->buffer_size) {
00332       wrote = bufferToOutput(str);
00333       if (wrote < 0) { return wrote; }
00334     }
00335   } while (read > 0 || wrote > 0);
00336 
00337   // Signal end of stream if finishing up, otherwise done
00338   if (action == ROSLZ4_FINISH) {
00339     return writeEOS(str);
00340   } else {
00341     return ROSLZ4_OK;
00342   }
00343 }
00344 
00345 void roslz4_compressEnd(roslz4_stream *str) {
00346   streamStateFree(str);
00347 }
00348 
00349 /*========================= Low level decompression =========================*/
00350 
00351 int roslz4_decompressStart(roslz4_stream *str) {
00352   return streamStateAlloc(str);
00353   // Can't allocate internal buffer, block size is unknown until header is read
00354 }
00355 
00356 // Return 1 if header is present, 0 if more data is needed,
00357 // LZ4 error code (< 0) if error
00358 int processHeader(roslz4_stream *str) {
00359   stream_state *state = str->state;
00360   if (str->total_in >= 7) {
00361     return 1;
00362   }
00363   // Populate header buffer
00364   int to_copy = min(7 - str->total_in, str->input_left);
00365   memcpy(state->header + str->total_in, str->input_next, to_copy);
00366   advanceInput(str, to_copy);
00367   if (str->total_in < 7) {
00368     return 0;
00369   }
00370 
00371   // Parse header buffer
00372   unsigned char *header = (unsigned char*) state->header;
00373   uint32_t magic_number = readUInt32(header);
00374   if (magic_number != kMagicNumber) {
00375     return ROSLZ4_DATA_ERROR; // Stream does not start with magic number
00376   }
00377   // Check descriptor flags
00378   int version                 = (header[4] >> 6) & k2Bits;
00379   int block_independence_flag = (header[4] >> 5) & k1Bits;
00380   int block_checksum_flag     = (header[4] >> 4) & k1Bits;
00381   int stream_size_flag        = (header[4] >> 3) & k1Bits;
00382   int stream_checksum_flag    = (header[4] >> 2) & k1Bits;
00383   int reserved1               = (header[4] >> 1) & k1Bits;
00384   int preset_dictionary_flag  = (header[4] >> 0) & k1Bits;
00385 
00386   int reserved2               = (header[5] >> 7) & k1Bits;
00387   int block_max_id            = (header[5] >> 4) & k3Bits;
00388   int reserved3               = (header[5] >> 0) & k4Bits;
00389 
00390   // LZ4 standard requirements
00391   if (version != 1) {
00392     return ROSLZ4_DATA_ERROR; // Wrong version number
00393   }
00394   if (reserved1 != 0 || reserved2 != 0 || reserved3 != 0) {
00395     return ROSLZ4_DATA_ERROR; // Reserved bits must be 0
00396   }
00397   if (!(4 <= block_max_id && block_max_id <= 7)) {
00398     return ROSLZ4_DATA_ERROR; // Invalid block size
00399   }
00400 
00401   // Implementation requirements
00402   if (stream_size_flag != 0) {
00403     return ROSLZ4_DATA_ERROR; // Stream size not supported
00404   }
00405   if (preset_dictionary_flag != 0) {
00406     return ROSLZ4_DATA_ERROR; // Dictionary not supported
00407   }
00408   if (block_independence_flag != 1) {
00409     return ROSLZ4_DATA_ERROR; // Block dependence not supported
00410   }
00411   if (block_checksum_flag != 0) {
00412     return ROSLZ4_DATA_ERROR; // Block checksums not supported
00413   }
00414   if (stream_checksum_flag != 1) {
00415     return ROSLZ4_DATA_ERROR; // Must have stream checksum
00416   }
00417 
00418   int header_checksum = (XXH32(header + 4, 2, 0) >> 8) & k8Bits;
00419   int stored_header_checksum = (header[6] >> 0) & k8Bits;
00420   if (header_checksum != stored_header_checksum) {
00421     return ROSLZ4_DATA_ERROR; // Header checksum doesn't match
00422   }
00423 
00424   int ret = streamResizeBuffer(str, block_max_id);
00425   if (ret == ROSLZ4_OK) {
00426     return 1;
00427   } else {
00428     return ret;
00429   }
00430 }
00431 
00432 // Read block size, return 1 if value is stored in state->block_size 0 otherwise
00433 int readBlockSize(roslz4_stream *str) {
00434   stream_state *state = str->state;
00435   if (state->block_size_read < 4) {
00436     fillUInt32(str, &state->block_size, &state->block_size_read);
00437     if (state->block_size_read == 4) {
00438       state->block_size = readUInt32((unsigned char*)&state->block_size);
00439       state->block_uncompressed = ((unsigned)state->block_size >> 31) & k1Bits;
00440       state->block_size &= 0x7FFFFFFF;
00441       DEBUG("readBlockSize() Block size = %i uncompressed = %i\n",
00442             state->block_size, state->block_uncompressed);
00443       return 1;
00444     } else {
00445       return 0;
00446     }
00447   }
00448   return 1;
00449 }
00450 
00451 // Copy at most one blocks worth of data from input to internal buffer.
00452 // Return 1 if whole block has been read, 0 if not, LZ4 error otherwise
00453 int readBlock(roslz4_stream *str) {
00454   stream_state *state = str->state;
00455   if (state->block_size_read != 4 || state->block_size == kEndOfStream) {
00456     return ROSLZ4_ERROR;
00457   }
00458 
00459   int block_left = state->block_size - state->buffer_offset;
00460   int to_copy = min(str->input_left, block_left);
00461   memcpy(state->buffer + state->buffer_offset, str->input_next, to_copy);
00462   advanceInput(str, to_copy);
00463   state->buffer_offset += to_copy;
00464   DEBUG("readBlock() Read %i bytes from input (block = %i/%i)\n",
00465         to_copy, state->buffer_offset, state->block_size);
00466   return state->buffer_offset == state->block_size;
00467 }
00468 
00469 int decompressBlock(roslz4_stream *str) {
00470   stream_state *state = str->state;
00471   if (state->block_size_read != 4 || state->block_size != state->buffer_offset) {
00472     // Internal error: Can't decompress block, it's not in buffer
00473     return ROSLZ4_ERROR;
00474   }
00475 
00476   if (state->block_uncompressed) {
00477     if (str->output_left >= state->block_size) {
00478       memcpy(str->output_next, state->buffer, state->block_size);
00479       int ret = XXH32_update(state->xxh32_state, str->output_next,
00480                              state->block_size);
00481       if (ret == XXH_ERROR) { return ROSLZ4_ERROR; }
00482       advanceOutput(str, state->block_size);
00483       state->block_size_read = 0;
00484       state->buffer_offset = 0;
00485       return ROSLZ4_OK;
00486     } else {
00487       return ROSLZ4_OUTPUT_SMALL;
00488     }
00489   } else {
00490     int decomp_size;
00491     decomp_size = LZ4_decompress_safe(state->buffer, str->output_next,
00492                                       state->block_size, str->output_left);
00493     if (decomp_size < 0) {
00494       if (str->output_left >= state->buffer_size) {
00495         return ROSLZ4_DATA_ERROR; // Must be a problem with the data stream
00496       } else {
00497         // Data error or output is small; increase output to disambiguate
00498         return ROSLZ4_OUTPUT_SMALL;
00499       }
00500     } else {
00501       int ret = XXH32_update(state->xxh32_state, str->output_next, decomp_size);
00502       if (ret == XXH_ERROR) { return ROSLZ4_ERROR; }
00503       advanceOutput(str, decomp_size);
00504       state->block_size_read = 0;
00505       state->buffer_offset = 0;
00506       return ROSLZ4_OK;
00507     }
00508   }
00509 }
00510 
00511 int readChecksum(roslz4_stream *str) {
00512   stream_state *state = str->state;
00513   fillUInt32(str, &state->stream_checksum, &state->stream_checksum_read);
00514   if (state->stream_checksum_read == 4) {
00515     state->finished = 1;
00516     state->stream_checksum = readUInt32((unsigned char*)&state->stream_checksum);
00517     uint32_t checksum = XXH32_digest(state->xxh32_state);
00518     state->xxh32_state = NULL;
00519     if (checksum == state->stream_checksum) {
00520       return ROSLZ4_STREAM_END;
00521     } else {
00522       return ROSLZ4_DATA_ERROR;
00523     }
00524   }
00525   return ROSLZ4_OK;
00526 }
00527 
00528 int roslz4_decompress(roslz4_stream *str) {
00529   stream_state *state = str->state;
00530   if (state->finished) {
00531     return ROSLZ4_ERROR; // Already reached end of stream
00532   }
00533 
00534   // Return if header isn't present or error was encountered
00535   int ret = processHeader(str);
00536   if (ret <= 0) {
00537     return ret;
00538   }
00539 
00540   // Read in blocks and decompress them as long as there's data to be processed
00541   while (str->input_left > 0) {
00542     ret = readBlockSize(str);
00543     if (ret == 0) { return ROSLZ4_OK; }
00544 
00545     if (state->block_size == kEndOfStream) {
00546       return readChecksum(str);
00547     }
00548 
00549     ret = readBlock(str);
00550     if (ret == 0) { return ROSLZ4_OK; }
00551     else if (ret < 0) { return ret; }
00552 
00553     ret = decompressBlock(str);
00554     if (ret < 0) { return ret; }
00555   }
00556   return ROSLZ4_OK;
00557 }
00558 
00559 void roslz4_decompressEnd(roslz4_stream *str) {
00560   streamStateFree(str);
00561 }
00562 
00563 /*=================== Oneshot compression / decompression ===================*/
00564 
00565 int roslz4_buffToBuffCompress(char *input, unsigned int input_size,
00566                               char *output, unsigned int *output_size,
00567                               int block_size_id) {
00568   roslz4_stream stream;
00569   stream.input_next = input;
00570   stream.input_left = input_size;
00571   stream.output_next = output;
00572   stream.output_left = *output_size;
00573 
00574   int ret;
00575   ret = roslz4_compressStart(&stream, block_size_id);
00576   if (ret != ROSLZ4_OK) { return ret; }
00577 
00578   while (stream.input_left > 0 && ret != ROSLZ4_STREAM_END) {
00579     ret = roslz4_compress(&stream, ROSLZ4_FINISH);
00580     if (ret == ROSLZ4_ERROR || ret == ROSLZ4_OUTPUT_SMALL) {
00581       roslz4_compressEnd(&stream);
00582       return ret;
00583     }
00584   }
00585 
00586   *output_size = *output_size - stream.output_left;
00587   roslz4_compressEnd(&stream);
00588 
00589   if (stream.input_left == 0 && ret == ROSLZ4_STREAM_END) {
00590     return ROSLZ4_OK; // Success
00591   } else {
00592     return ROSLZ4_ERROR; // User did not provide exact buffer
00593   }
00594 }
00595 
00596 int roslz4_buffToBuffDecompress(char *input, unsigned int input_size,
00597                                 char *output, unsigned int *output_size) {
00598   roslz4_stream stream;
00599   stream.input_next = input;
00600   stream.input_left = input_size;
00601   stream.output_next = output;
00602   stream.output_left = *output_size;
00603 
00604   int ret;
00605   ret = roslz4_decompressStart(&stream);
00606   if (ret != ROSLZ4_OK) { return ret; }
00607 
00608   while (stream.input_left > 0 && ret != ROSLZ4_STREAM_END) {
00609     ret = roslz4_decompress(&stream);
00610     if (ret < 0) {
00611       roslz4_decompressEnd(&stream);
00612       return ret;
00613     }
00614   }
00615 
00616   *output_size = *output_size - stream.output_left;
00617   roslz4_decompressEnd(&stream);
00618 
00619   if (stream.input_left == 0 && ret == ROSLZ4_STREAM_END) {
00620     return ROSLZ4_OK; // Success
00621   } else {
00622     return ROSLZ4_ERROR; // User did not provide exact buffer
00623   }
00624 }


roslz4
Author(s): Ben Charrow
autogenerated on Thu Jun 6 2019 21:09:51