The following errata report has been submitted for RFC9204,
"QPACK: Field Compression for HTTP/3".

--------------------------------------
You may review the report below and at:
https://www.rfc-editor.org/errata/eid8410

--------------------------------------
Type: Technical
Reported by: Kazu Yamamoto <k...@iij.ad.jp>

Section: Appendix C

Original Text
-------------
base = dynamicTable.getInsertCount()
requiredInsertCount = 0
for line in fieldLines:
  staticIndex = staticTable.findIndex(line)
  if staticIndex is not None:
    encodeStaticIndexReference(streamBuffer, staticIndex)
    continue

  dynamicIndex = dynamicTable.findIndex(line)
  if dynamicIndex is None:
    # No matching entry.  Either insert+index or encode literal
    staticNameIndex = staticTable.findName(line.name)
    if staticNameIndex is None:
       dynamicNameIndex = dynamicTable.findName(line.name)

    if shouldIndex(line) and dynamicTable.canIndex(line):
      encodeInsert(encoderBuffer, staticNameIndex,
                   dynamicNameIndex, line)
      dynamicIndex = dynamicTable.add(line)

  if dynamicIndex is None:
    # Could not index it, literal
    if dynamicNameIndex is not None:
      # Encode literal with dynamic name, possibly above Base
      encodeDynamicLiteral(streamBuffer, dynamicNameIndex,
                           base, line)
      requiredInsertCount = max(requiredInsertCount,
                                dynamicNameIndex)
    else:
      # Encodes a literal with a static name or literal name
      encodeLiteral(streamBuffer, staticNameIndex, line)
  else:
    # Dynamic index reference
    assert(dynamicIndex is not None)
    requiredInsertCount = max(requiredInsertCount, dynamicIndex)
    # Encode dynamicIndex, possibly above Base
    encodeDynamicIndexReference(streamBuffer, dynamicIndex, base)

# encode the prefix
if requiredInsertCount == 0:
  encodeInteger(prefixBuffer, 0x00, 0, 8)
  encodeInteger(prefixBuffer, 0x00, 0, 7)
else:
  wireRIC = (
    requiredInsertCount
    % (2 * getMaxEntries(maxTableCapacity))
  ) + 1;
  encodeInteger(prefixBuffer, 0x00, wireRIC, 8)
  if base >= requiredInsertCount:
    encodeInteger(prefixBuffer, 0x00,
                  base - requiredInsertCount, 7)
  else:
    encodeInteger(prefixBuffer, 0x80,
                  requiredInsertCount - base - 1, 7)

return encoderBuffer, prefixBuffer + streamBuffer

Corrected Text
--------------
base = dynamicTable.getInsertCount()
requiredInsertCount = 0
for line in fieldLines:
  staticIndex = staticTable.findIndex(line)
  if staticIndex is not None:
    encodeStaticIndexReference(streamBuffer, staticIndex)
    continue

  dynamicIndex = dynamicTable.findIndex(line)
  if dynamicIndex is None:
    # No matching entry.  Either insert+index or encode literal
    staticNameIndex = staticTable.findName(line.name)
    if staticNameIndex is None:
       dynamicNameIndex = dynamicTable.findName(line.name)

    if shouldIndex(line) and dynamicTable.canIndex(line):
      encodeInsert(encoderBuffer, staticNameIndex,
                   dynamicNameIndex, line)
      dynamicIndex = dynamicTable.add(line)

  if dynamicIndex is None:
    # Could not index it, literal
    if dynamicNameIndex is not None:
      # Encode literal with dynamic name, possibly above Base
      encodeDynamicLiteral(streamBuffer, dynamicNameIndex,
                           base, line)
      requiredInsertCount = max(requiredInsertCount,
                                dynamicNameIndex + 1)
    else:
      # Encodes a literal with a static name or literal name
      encodeLiteral(streamBuffer, staticNameIndex, line)
  else:
    # Dynamic index reference
    assert(dynamicIndex is not None)
    requiredInsertCount = max(requiredInsertCount, dynamicIndex + 1)
    # Encode dynamicIndex, possibly above Base
    encodeDynamicIndexReference(streamBuffer, dynamicIndex, base)

# encode the prefix
if requiredInsertCount == 0:
  encodeInteger(prefixBuffer, 0x00, 0, 8)
  encodeInteger(prefixBuffer, 0x00, 0, 7)
else:
  wireRIC = (
    requiredInsertCount
    % (2 * getMaxEntries(maxTableCapacity))
  ) + 1;
  encodeInteger(prefixBuffer, 0x00, wireRIC, 8)
  if base >= requiredInsertCount:
    encodeInteger(prefixBuffer, 0x00,
                  base - requiredInsertCount, 7)
  else:
    encodeInteger(prefixBuffer, 0x80,
                  requiredInsertCount - base - 1, 7)

return encoderBuffer, prefixBuffer + streamBuffer


Notes
-----
"Sample Single-Pass Encoding Algorithm" in Appendix C has a bug that Count is 
equel to the index. Count must be one higher than index.

Reasoning the code:

requiredInsertCount is initialized with 0:

    requiredInsertCount = 0

dynamicIndex can be 0 if it is the first entry of the dynamic table:

    dynamicIndex = dynamicTable.findIndex(line)

In this case, requiredInsertCount stay with 0:

   requiredInsertCount = max(requiredInsertCount, dynamicIndex)

This results in a wrong prefix:

    if requiredInsertCount == 0:
      encodeInteger(prefixBuffer, 0x00, 0, 8)
      encodeInteger(prefixBuffer, 0x00, 0, 7)

The following code is correct:

    requiredInsertCount = max(requiredInsertCount, dynamicIndex + 1)

Instructions:
-------------
This erratum is currently posted as "Reported". (If it is spam, it 
will be removed shortly by the RFC Production Center.) Please
use "Reply All" to discuss whether it should be verified or
rejected. When a decision is reached, the verifying party  
will log in to change the status and edit the report, if necessary.

--------------------------------------
RFC9204 (draft-ietf-quic-qpack-21)
--------------------------------------
Title               : QPACK: Field Compression for HTTP/3
Publication Date    : June 2022
Author(s)           : C. Krasic, M. Bishop, A. Frindell, Ed.
Category            : PROPOSED STANDARD
Source              : QUIC
Stream              : IETF
Verifying Party     : IESG

Reply via email to