int ZLib::UncompressAtMostOrAll()

in app/rest/zlibwrapper.cc [504:681]


int ZLib::UncompressAtMostOrAll(Bytef* dest, uLongf* destLen,
                                const Bytef* source, uLong* sourceLen,
                                int flush_mode) {  // Z_SYNC_FLUSH or Z_FINISH
  int err = Z_OK;

  if (first_chunk_) {
    gzip_footer_bytes_ = -1;
    if (settings_.gzip_header_mode_) {
      // If we haven't read our first chunk of actual compressed data,
      // and we're expecting gzip headers, then parse some more bytes
      // from the gzip headers.
      const Bytef* bodyBegin = nullptr;
      GZipHeader::Status status = gzip_header_->ReadMore(
          reinterpret_cast<const char*>(source), *sourceLen,
          reinterpret_cast<const char**>(&bodyBegin));
      switch (status) {
        case GZipHeader::INCOMPLETE_HEADER:  // don't have the complete header
          *destLen = 0;
          return Z_OK;
        case GZipHeader::INVALID_HEADER:  // bogus header
          Reset();
          return Z_DATA_ERROR;
        case GZipHeader::COMPLETE_HEADER:      // we have the full header
          *sourceLen -= (bodyBegin - source);  // skip past header bytes
          source = bodyBegin;
          crc_ = crc32(0, nullptr, 0);  // initialize CRC
          break;
        default:
          LogError("Unexpected gzip header parsing result: %s", status);
      }
    }
  } else if (gzip_footer_bytes_ >= 0) {
    // We're now just reading the gzip footer. We already read all the data.
    if (gzip_footer_bytes_ + *sourceLen > sizeof(gzip_footer_) &&
        // When this flag is true, we allow some extra bytes after the
        // gzip footer.
        !should_be_flexible_with_gzip_footer_) {
      LogDebug(
          "UncompressChunkOrAll: Received %d extra bytes after gzip footer: %s",
          gzip_footer_bytes_ + *sourceLen - sizeof(gzip_footer_),
          reinterpret_cast<const char*>(source), std::min(*sourceLen, 20UL));
      Reset();
      return Z_DATA_ERROR;
    }
    uLong len = sizeof(gzip_footer_) - gzip_footer_bytes_;
    if (len > *sourceLen) len = *sourceLen;
    if (len > 0) {
      memcpy(gzip_footer_ + gzip_footer_bytes_, source, len);
      gzip_footer_bytes_ += len;
    }
    *destLen = 0;
    return Z_OK;
  }

  if ((err = UncompressInit(dest, destLen, source, sourceLen)) != Z_OK) {
    LogWarning("ZLib: UncompressInit: Error: %d SourceLen: %d", err,
               *sourceLen);
    return err;
  }

  // This is used to figure out how many output bytes we wrote *this chunk*:
  const uLong old_total_out = uncomp_stream_.total_out;

  // This is used to figure out how many input bytes we read *this chunk*:
  const uLong old_total_in = uncomp_stream_.total_in;

  // Some setup happens only for the first chunk we compress in a run
  if (first_chunk_) {
    // Initialize the dictionary just before we start compressing
    if (settings_.gzip_header_mode_ || settings_.no_header_mode_) {
      // In no_header_mode, we can just set the dictionary, since no
      // checking is done to advance past header bits to get us in the
      // dictionary setting mode. In settings_.gzip_header_mode_ we've already
      // removed headers, so this code works too.
      if (settings_.dictionary_) {
        err = inflateSetDictionary(&uncomp_stream_, settings_.dictionary_,
                                   settings_.dict_len_);
        if (err != Z_OK) {
          LogWarning("inflateSetDictionary: Error: %d dict_len: %d", err,
                     settings_.dict_len_);
          UncompressErrorInit();
          return err;
        }
        init_settings_.dictionary_ = settings_.dictionary_;
        init_settings_.dict_len_ = settings_.dict_len_;
      }
    }

    first_chunk_ = false;  // so we don't do this again

    // For the first chunk *only* (to avoid infinite troubles), we let
    // there be no actual data to uncompress.  This sometimes triggers
    // when the input is only the gzip header, say.
    if (*sourceLen == 0) {
      *destLen = 0;
      return Z_OK;
    }
  }

  // We'll uncompress as much as we can.  If we end OK great, otherwise
  // if we get an error that seems to be the gzip footer, we store the
  // gzip footer and return OK, otherwise we return the error.

  // flush_mode is Z_SYNC_FLUSH for chunked mode, Z_FINISH for all mode.
  err = inflate(&uncomp_stream_, flush_mode);
  if (settings_.dictionary_ && err == Z_NEED_DICT) {
    err = inflateSetDictionary(&uncomp_stream_, settings_.dictionary_,
                               settings_.dict_len_);
    if (err != Z_OK) {
      LogWarning("UncompressChunkOrAll: failed in inflateSetDictionary :%d ",
                 err);
      UncompressErrorInit();
      return err;
    }
    init_settings_.dictionary_ = settings_.dictionary_;
    init_settings_.dict_len_ = settings_.dict_len_;
    err = inflate(&uncomp_stream_, flush_mode);
  }

  // Figure out how many bytes of the input zlib slurped up:
  const uLong bytes_read = uncomp_stream_.total_in - old_total_in;
  FIREBASE_ASSERT(source + bytes_read <= source + *sourceLen);
  *sourceLen = uncomp_stream_.avail_in;

  // Next we look at the footer, if any. Note that we might currently
  // have just part of the footer (eg, if this data is arriving over a
  // socket). After looking for a footer, log a warning if there is
  // extra cruft.
  if ((err == Z_STREAM_END) &&
      ((gzip_footer_bytes_ == -1) ||
       (gzip_footer_bytes_ < sizeof(gzip_footer_))) &&
      (uncomp_stream_.avail_in <= sizeof(gzip_footer_) ||
       // When this flag is true, we allow some extra bytes after the
       // zlib footer.
       should_be_flexible_with_gzip_footer_)) {
    // Due to a bug in old versions of zlibwrapper, we appended the gzip
    // footer even in non-gzip mode.  Thus we always allow a gzip footer
    // even if we're not in gzip mode, so we can continue to uncompress
    // the old data.  :-(

    // Store gzip footer bytes so we can check for footer consistency
    // in UncompressChunkDone(). (If we have the whole footer, we
    // could do the checking here, but we don't to keep consistency
    // with CompressChunkDone().)
    gzip_footer_bytes_ = std::min(static_cast<size_t>(uncomp_stream_.avail_in),
                                  sizeof(gzip_footer_));
    memcpy(gzip_footer_, source + bytes_read, gzip_footer_bytes_);
  } else if ((err == Z_STREAM_END || err == Z_OK)  // everything went ok
             && uncomp_stream_.avail_in == 0) {    // and we read it all
    ;
  } else if (err == Z_STREAM_END && uncomp_stream_.avail_in > 0) {
    LogDebug(
        "UncompressChunkOrAll: Received some extra data, bytes total: %d "
        "bytes: %s",
        uncomp_stream_.avail_in,
        reinterpret_cast<const char*>(uncomp_stream_.next_in));
    UncompressErrorInit();
    return Z_DATA_ERROR;  // what's the extra data for?
  } else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) {
    // an error happened
    LogDebug("UncompressChunkOrAll: Error: %d avail_out: %d", err,
             uncomp_stream_.avail_out);
    UncompressErrorInit();
    return err;
  } else if (uncomp_stream_.avail_out == 0) {
    err = Z_BUF_ERROR;
  }

  assert(err == Z_OK || err == Z_BUF_ERROR || err == Z_STREAM_END);
  if (err == Z_STREAM_END && !settings_.dont_hide_zstream_end_) err = Z_OK;

  // update the crc and other metadata
  uncompressed_size_ = uncomp_stream_.total_out;
  *destLen = uncomp_stream_.total_out - old_total_out;  // size for this call
  if (settings_.gzip_header_mode_) crc_ = crc32(crc_, dest, *destLen);

  return err;
}