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

#ifndef NET_WEBSOCKETS_WEBSOCKET_INFLATER_H_
#define NET_WEBSOCKETS_WEBSOCKET_INFLATER_H_

#include <stddef.h>

#include <memory>
#include <utility>
#include <vector>

#include "base/containers/circular_deque.h"
#include "base/memory/scoped_refptr.h"
#include "net/base/net_export.h"

extern "C" struct z_stream_s;

namespace net {

class IOBufferWithSize;

// WebSocketInflater uncompresses data compressed by DEFLATE algorithm.
class NET_EXPORT_PRIVATE WebSocketInflater {
 public:
  WebSocketInflater();
  // `input_queue_capacity` is a capacity for each contiguous block in the
  // input queue. The input queue can grow without limit.
  WebSocketInflater(size_t input_queue_capacity, size_t output_buffer_capacity);

  WebSocketInflater(const WebSocketInflater&) = delete;
  WebSocketInflater& operator=(const WebSocketInflater&) = delete;

  ~WebSocketInflater();

  // Returns true if there is no error.
  // `window_bits` must be between 8 and 15 (both inclusive).
  // This function must be called exactly once before calling any of the
  // following functions.
  bool Initialize(int window_bits);

  // Adds bytes from the given span `data` to `stream_`.
  // Returns true if there is no error.
  // If the size of the output data reaches the capacity of the output buffer,
  // the following input data will be "choked", i.e. stored in the input queue,
  // staying compressed.
  bool AddBytes(base::span<const uint8_t> data);

  // Flushes the input.
  // Returns true if there is no error.
  bool Finish();

  // Returns up to `size` bytes of the decompressed output.
  // Returns null if there is an inflation error.
  // The returned bytes will be dropped from the current output and never be
  // returned again.
  // If some input data is choked, calling this function may restart the
  // inflation process.
  // This means that even if you call `Finish()` and call `GetOutput()` with
  // size = `CurrentOutputSize()`, the inflater may have some remaining data.
  // To confirm the inflater emptiness, you should check whether
  // `CurrentOutputSize()` is zero.
  scoped_refptr<IOBufferWithSize> GetOutput(size_t size);

  // Returns the size of the current inflated output.
  size_t CurrentOutputSize() const { return output_buffer_.Size(); }

  static constexpr size_t kDefaultBufferCapacity = 512;
  static constexpr size_t kDefaultInputIOBufferCapacity = 512;

 private:
  // Ring buffer with fixed capacity.
  class NET_EXPORT_PRIVATE OutputBuffer {
   public:
    explicit OutputBuffer(size_t capacity);
    ~OutputBuffer();

    size_t Size() const;
    // Returns a span representing the writable tail area.
    // A user can push data to the queue by writing the data to
    // the area returned by this function and calling AdvanceTail.
    base::span<uint8_t> GetTail();
    void Read(base::span<uint8_t> dest);
    void AdvanceTail(size_t advance);

   private:
    void AdvanceHead(size_t advance);

    const size_t capacity_;
    std::vector<uint8_t> buffer_;
    size_t head_ = 0;
    size_t tail_ = 0;
  };

  class InputQueue {
   public:
    // `capacity` is used for the capacity of each IOBuffer in this queue.
    // this queue itself can grow without limit.
    explicit InputQueue(size_t capacity);
    ~InputQueue();

    // Returns a span representing the first component of unconsumed data
    base::span<const uint8_t> Top();
    bool IsEmpty() const { return buffers_.empty(); }
    void Push(base::span<const uint8_t> data);
    // Consumes the topmost `size` bytes.
    // `size` must be less than or equal to the first buffer size.
    void Consume(size_t size);

   private:
    void TakeAndPushToLastBuffer(base::span<const uint8_t>& data);

    const size_t capacity_;
    size_t head_of_first_buffer_ = 0;
    size_t tail_of_last_buffer_ = 0;
    base::circular_deque<scoped_refptr<IOBufferWithSize>> buffers_;
  };

  int InflateWithFlush(base::span<const uint8_t> next_in);
  int Inflate(base::span<const uint8_t> next_in, int flush);
  int InflateExistingInput(int flush);
  int InflateChokedInput();

  std::unique_ptr<z_stream_s> stream_;
  InputQueue input_queue_;
  OutputBuffer output_buffer_;
};

}  // namespace net

#endif  // NET_WEBSOCKETS_WEBSOCKET_INFLATER_H_