910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "zst_compressor.h"

#include <iostream>

#include "third_party/zstd/src/lib/zstd.h"

ZstCompressor::ZstCompressor(std::ostream& output_stream, int compression_level)
    : output_stream_(output_stream) {
  input_buffer_recommended_size_ = ZSTD_CStreamInSize();
  input_struct_ = new ZSTD_inBuffer{/* src = */ nullptr,
                                    /* size = */ 0,
                                    /* pos = */ 0};
  output_buffer_size_ = ZSTD_CStreamOutSize();
  output_buffer_ = new char[output_buffer_size_];
  output_struct_ = new ZSTD_outBuffer{/* dst = */ output_buffer_,
                                      /* size = */ output_buffer_size_,
                                      /* pos = */ 0};
  ctx_ = ZSTD_createCCtx();
  if (ctx_ == nullptr) {
    std::cerr << "ZSTD_createCCtx failed!" << std::endl;
    exit(1);
  }
  size_t ret =
      ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, compression_level);
  if (ZSTD_isError(ret)) {
    std::cerr << "ZSTD_CCtx_setParameter returned an error: "
              << ZSTD_getErrorName(ret) << std::endl;
    exit(1);
  }
}

ZstCompressor::~ZstCompressor() {
  delete input_struct_;
  delete[] output_buffer_;
  delete output_struct_;
  ZSTD_freeCCtx(ctx_);
}

size_t ZstCompressor::GetRecommendedInputBufferSize() {
  return input_buffer_recommended_size_;
}

void ZstCompressor::CompressStreaming(UncompressedContent input,
                                      bool last_chunk) {
  input_struct_->src = input.buffer;
  input_struct_->size = input.size;
  input_struct_->pos = 0;
  ZSTD_EndDirective mode = last_chunk ? ZSTD_e_end : ZSTD_e_continue;

  // Repeatedly call ZSTD_compressStream2() until it has consumed all the input.
  bool finished = false;
  while (!finished) {
    output_struct_->pos = 0;
    size_t ret =
        ZSTD_compressStream2(ctx_, output_struct_, input_struct_, mode);
    if (ZSTD_isError(ret)) {
      std::cerr << "ZSTD_compressStream returned an error: "
                << ZSTD_getErrorName(ret) << std::endl;
      exit(1);
    }
    output_stream_.write(output_buffer_, output_struct_->pos);
    if (output_stream_.fail()) {
      std::cerr << "ZST compressor failed to write to output_stream."
                << std::endl;
      exit(1);
    }
    // If we are processing the last chunk, we are finished when zstd returns 0,
    // which means it has consumed all the input AND flushed all the output.
    // Otherwise, we are finished when it has consumed all the input.
    if (last_chunk) {
      finished = (ret == 0);
    } else {
      finished = (input_struct_->pos == input.size);
    }
  }
}