All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.http4s.hpack.DynamicTable.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2022 http4s.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright 2014 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.http4s.hpack;

import org.http4s.hpack.HeaderField.HEADER_ENTRY_OVERHEAD;

private[http4s] final class DynamicTable {

  // a circular queue of header fields
  var headerFields: Array[HeaderField] = _;
  var head: Int = _;
  var tail: Int = _;
  var size: Int = _;
  var capacity = -1; // ensure setCapacity creates the array

  /** Creates a new dynamic table with the specified initial capacity.
    */
  def this(initialCapacity: Int) {
    this()
    setCapacity(initialCapacity);
  }

  /** Return the number of header fields in the dynamic table.
    */
  def length(): Int = {
    var length: Int = 0;
    if (head < tail) {
      length = headerFields.length - tail + head;
    } else {
      length = head - tail;
    }
    return length;
  }

  /** Return the header field at the given index.
    * The first and newest entry is always at index 1,
    * and the oldest entry is at the index length().
    */
  def getEntry(index: Int): HeaderField = {
    if (index <= 0 || index > length()) {
      throw new IndexOutOfBoundsException();
    }
    val i = head - index;
    if (i < 0) {
      return headerFields(i + headerFields.length);
    } else {
      return headerFields(i);
    }
  }

  /** Add the header field to the dynamic table.
    * Entries are evicted from the dynamic table until the size of the table
    * and the new header field is less than or equal to the table's capacity.
    * If the size of the new entry is larger than the table's capacity,
    * the dynamic table will be cleared.
    */
  def add(header: HeaderField): Unit = {
    val headerSize = header.size();
    if (headerSize > capacity) {
      clear();
      return;
    }
    while (size + headerSize > capacity)
      remove();
    headerFields(head) = header;
    head += 1
    size += header.size();
    if (head == headerFields.length) {
      head = 0;
    }
  }

  /** Remove and return the oldest header field from the dynamic table.
    */
  def remove(): HeaderField = {
    val removed = headerFields(tail);
    if (removed == null) {
      return null;
    }
    size -= removed.size();
    headerFields(tail) = null;
    tail += 1
    if (tail == headerFields.length) {
      tail = 0;
    }
    return removed;
  }

  /** Remove all entries from the dynamic table.
    */
  def clear(): Unit = {
    while (tail != head) {
      headerFields(tail) = null;
      tail += 1
      if (tail == headerFields.length) {
        tail = 0;
      }
    }
    head = 0;
    tail = 0;
    size = 0;
  }

  /** Set the maximum size of the dynamic table.
    * Entries are evicted from the dynamic table until the size of the table
    * is less than or equal to the maximum size.
    */
  def setCapacity(capacity: Int): Unit = {
    if (capacity < 0) {
      throw new IllegalArgumentException("Illegal Capacity: " + capacity);
    }

    // initially capacity will be -1 so init won't return here
    if (this.capacity == capacity) {
      return;
    }
    this.capacity = capacity;

    if (capacity == 0) {
      clear();
    } else {
      // initially size will be 0 so remove won't be called
      while (size > capacity)
        remove();
    }

    var maxEntries = capacity / HEADER_ENTRY_OVERHEAD;
    if (capacity % HEADER_ENTRY_OVERHEAD != 0) {
      maxEntries += 1;
    }

    // check if capacity change requires us to reallocate the array
    if (headerFields != null && headerFields.length == maxEntries) {
      return;
    }

    var tmp = new Array[HeaderField](maxEntries);

    // initially length will be 0 so there will be no copy
    val len = length();
    var cursor = tail;
    var i = 0;
    while (i < len) {
      val entry = headerFields(cursor);
      cursor += 1
      tmp(i) = entry;
      if (cursor == headerFields.length) {
        cursor = 0;
      }
      i += 1
    }

    this.tail = 0;
    this.head = tail + len;
    this.headerFields = tmp;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy